问题发生的场景:页面渲染时,会加载一个n行的列表,列表每一行对应一组数据,一组数据由一个请求获得。我们需要将请求获取到的数据按照一定顺序显示到页面上
下面先看一下数据大致的请求与存储方式:
dataSource = [obj1, obj2, objn];
getData = (dataSource) => {
let selectArr = [];
let { title } = this.props;
_.map(dataSource, async (item, index) => {
// 请求数据
await getBasicPlans(title, item.id).then(data => {
// 将数据按照请求时的顺序保存进数组(optionsArr是一个数组,里面保存着option中的一条条数据)
selectArr.splice(index, 0, data.optionsArr);
this.setState({ selectArr });
})
})
}
可以看到,我们将数据存储进数组时,使用了遍历时的index,并且还有await进行同步化的处理。按理来说,请求完了第一条数据才会请求请求第二条数据,这样一来,splice中的index就是从0→1→2依次增大,那么selectArr中存储的数据就是有序的
但结果确是selectArr中存储的数据是无序的
我们在.then中打印index来看一下:
可以看到,并没有按照我们预想的那样按照顺序将index打印出来
map/forEach内部使用了while结合callback方式来执行函数,await不会等待callback的执行
可以这样写
await Promise.all(
a.map(dataSource, (item, index) => {
// 请求数据
await getBasicPlans(title, item.id).then(data => {
// 将数据按照请求时的顺序保存进数组(optionsArr是一个数组,里面保存着option中的一条条数据)
selectArr.splice(index, 0, data.optionsArr);
this.setState({ selectArr });
})
})
);
这样写一点好处都没有,会导致速度慢,影响客户体验
这个async位置应该在map里面吧,可以试试这样可不可以
_.map(dataSource, async (item, index) => {
// 请求数据
await getBasicPlans(title, item.id).then(data => {
// 将数据按照请求时的顺序保存进数组(optionsArr是一个数组,里面保存着option中的一条条数据)
selectArr.splice(index, 0, data.optionsArr);
this.setState({ selectArr });
})
})
对你有用的话,请点击一下【采纳此答案】,谢谢🌹
我的理解,既然你是使用async/await关键字 await不可以单独使用 一定要跟着async
一般是这样用 async A(){ await ....} 或者 const A = async ()=>{await... } 这想必你都知道
_.map(dataSource, (item, index) => {})那这里的 (item, index) => {}是不是一个函数 那么你在这个函数里面用了await 关键字 ,那是不是应该也必须得有async
不知这样加可不可以
getData = async (dataSource) => {
let selectArr = [];
let { title } = this.props;
await _.map(dataSource, **async** (item, index) => {
// 请求数据
await getBasicPlans(title, item.id).then(data => {
// 将数据按照请求时的顺序保存进数组(optionsArr是一个数组,里面保存着option中的一条条数据)
selectArr.splice(index, 0, data.optionsArr);
this.setState({ selectArr });
})
})
}
}
map 会创建出多个回调函数,多个回调函数被加上了各自的 async、await:
// map遍历得到的结果
async ()=>{
await getBasicPlans(); // getBasicPlans的返回值是一个 Promise 对象
}
async ()=>{
await getBasicPlans();
}
async ()=>{
await getBasicPlans();
}
各个函数之间是独立的,彼此的回调也是独立的
请求是异步的,彼此之间又没有关联,顺序也就自然无法保证
如果返回的数据是这样的(类似于 for 循环的使用结果):
// for 循环产生的一个个异步请求之间的关系
await getBasicPlans();
await getBasicPlans();
await getBasicPlans();
可以在一定程度解决 “顺序” 的目的
但是这样的方式,就需要等待上一个请求响应了,才能够发送下一个请求。花费的时间总量等于各个请求所消耗的时间之和。如果请求较多,这种方式就会非常占用时间
可以看的出来,这种方式在实现方式上,就是说依旧是同步的。
最完美的解决方案是使用 promise.all
async ()=>{
promise.all(arr.map(() => {
return getBasicPlans();
})).then(data => {
data...
})
}
通过 map 遍历得到一个个的 Promise 实例化对象
async ()=>{
promise.all([promise1, promise2, promise3 ... ]).then(data => {
data...
})
}
由 promise.all 的实现机制,数组内后一个 promise 的返回不需要依赖前一个 promise 的状态,只需要保证生成 promise 对象的函数是有序的即可
恰巧,map 就只能保证依次去创建并执行回调函数,而不会去在意结果返回
我们知道,map 创建的回调函数是并行执行的,那么,promise 数组创建完成的时间,就由消耗时间最久的请求决定
接下来再依次去拿着数组中的每一个 promise 去执行 .then
可以看到,在这种场景下,promise.all 结合着 map 来使用,异步并行的同时,还可以完美的保证顺序,的确是一个非常完美的解决方案。