map 中使用 await 导致输出顺序失效

489 阅读2分钟

在开发中遇到这样的一个问题,在map里面使用await,导致输出的结果顺序乱序。

正确的输出结果:

1-0.png

1-1.png

错误的结果输出:

2-0.png

2-1.png

// **** 原代码 ****
// 将 await 放入 map 中的代码实例
approvePeopleList.map(async (item) => {
    const data = {
        "pageNo": 1,
        "pageSize": 10,
        "orgId": item.userId
    }
    try {
        const res = await orguser(data)
        orguserList.value.push(res.data.records[0]?.name ?? '暂无数据')
    } catch (error) {
        console.log(error);
    }
})

为什么造成这样的结果?

查询了 MDN 中对 map 方法的描述,是这样描述的。map 方法会给原数组中的每个元素都按顺序调用一次 callbackFn 函数。

按照这个描述,map 会创建多个回调函数,每个回调函数上会添加上 async、await:

// map 遍历后会按顺序调用回调函数
async (item) => {
    const data = {
        "pageNo": 1,
        "pageSize": 10,
        "orgId": item.userId
    }
    const res = await orguser(data)
    orguserList.value.push(item.data.records[0]?.name ?? '暂无数据')
}
​
async (item) => {
    const data = {
        "pageNo": 1,
        "pageSize": 10,
        "orgId": item.userId
    }
    const res = await orguser(data)
    orguserList.value.push(item.data.records[0]?.name ?? '暂无数据')
}
​
async (item) => {
    const data = {
        "pageNo": 1,
        "pageSize": 10,
        "orgId": item.userId
    }
    const res = await orguser(data)
    orguserList.value.push(item.data.records[0]?.name ?? '暂无数据')
}

造成乱序的原因:

由于各个回调函数之间的关系是独立的,所以他们的执行顺序是无法保证的,进而导致我们的返回结果造成乱序。

解决办法:

最终想到了 promise.all 来解决,因为 promise.all 接收一个数组类型,输入的所有 promise 的 resolve 回调的结果是一个数组。而且他的机制是,数组内的后一个 promise 的返回不需要依赖前一个 promise 的状态,只需要保证生成 promise 对象的函数是有序的即可。恰巧,map 就只能保证依次去创建并执行回调函数,而不会去在意结果返回。

// **** 修改后的代码 ****
// 收集所有异步任务到 list 数组中
const list = approvePeopleList.map(async (item) => {
    const data = {
        "pageNo": 1,
        "pageSize": 10,
        "orgId": item.userId
    }
    const res = await orguser(data)
    return res
})
// map 返回的 promise 进行集中处理, map 得到了一个个的 Promise 实例化对象
Promise.all(list).then((res) => {  // list => [promse1, promsise2, promise3]
    console.log(res);
​
    res.map((item: any) => {
        orguserList.value.push(item.data.records[0]?.name ?? '暂无数据')
    })
})

优势:

而且这样做的好处是大大提升了时间复杂度,由于map 创建的回调函数是并行执行的,所以 promise 数组创建完成的时间,就由消耗时间最久的请求决定。