【问题标题】:Async await in map function not executing in order地图函数中的异步等待未按顺序执行
【发布时间】:2021-05-07 04:42:58
【问题描述】:

我有以下代码:

var arr = [
    { condition: true, recursion: [], result: 'OLD_RESULT', recursiveArray: [{condition: true, result: 'OLD_RESULT'}] },
    {condition: false, result: 'OLD_RESULT'},
    { condition: true, recursion: [], result: 'OLD_RESULT', recursiveArray: [{condition: true, result: 'OLD_RESULT'}] },
    {condition: false, result: 'OLD_RESULT'},
];

var someAsyncCall = async () => {
    return 'NEW VALUE';
};

var asyncInMap = (arr) => {
    return arr.map(async (elem) => {
        const condition = elem.condition;
        if (condition) {
            const result = await someAsyncCall();
            elem.result = result;
        }
        if (elem.recursiveArray) {
            elem.recursion = asyncInMap(elem.recursiveArray);
        }
        console.log('SECOND:', elem);
        return elem;
    });
};

console.log('FIRST');
var promisesVal = asyncInMap(arr);
console.log('THIRD');
var completedVal = await Promise.all(promisesVal);
console.log('FOURTH', completedVal);

我的问题按我收到的console.log's 的顺序排列:

FIRST
SECOND: {condition: false, result: "OLD_RESULT"}
SECOND: {condition: false, result: "OLD_RESULT"}
THIRD
SECOND: {condition: true, recursion: Array(1), result: "NEW VALUE", recursiveArray: Array(1)}
SECOND: {condition: true, recursion: Array(1), result: "NEW VALUE", recursiveArray: Array(1)}
SECOND: {condition: true, result: "NEW VALUE"}
SECOND: {condition: true, result: "NEW VALUE"}
FOURTH (4) [{…}, {…}, {…}, {…}]

为什么在所有Second console.log 完成之前打印Third 日志?这是因为我在asyncInMap 函数中使用递归以及async/await?理想情况下应该完成asyncInMap(首先打印所有Second 日志),然后只打印正确的Third 日志?

【问题讨论】:

  • 如果你不勾选“使用 BabelJS”选项,它会在 StackOverflow 中运行。这不是必需的,因为大多数浏览器都支持 ES6,尽管没有任何记录。
  • @Barmar 在 Safari 中,我相信我需要检查一下。但是当你将它粘贴到支持 ES6 的浏览器中时,你是否碰巧知道为什么日志会以这种顺序出现?

标签: javascript async-await


【解决方案1】:

这就是js中async await代码的特点。 THIRD 将在该调用中的任何异步操作之前被调用,因为事件循环的工作方式与 js 中的一样。但是您的代码中存在一个错误,可以使 FOURTH 在所有 SECONDs 完成之前被记录,因为您没有等到递归 SECONDs 在任何地方完成。要解决这个问题,您可以在此处添加等待

elem.recursion = await Promise.all(asyncInMap(elem.recursiveArray));

如果你想保持执行顺序,你可以做简单的迭代,在函数顶层做所有的等待逻辑

var asyncInMap = async (arr) => {
     for(const elem of arr) {
       if(elem.condition) {
         elem.result = await someAsyncCall();
       }
       if(elem.recursiveArray) elem.recursion = await asyncInMap(elem.recursiveArray);
       console.log('SECOND:', elem);
     }
     return arr;
};



console.log('FIRST');
var promisesVal = asyncInMap(arr);
console.log('THIRD', 'calls can still be in progress here');
var completedVal = await promisesVal;
console.log('FOURTH', 'everything is completed here', completedVal);

请注意,在最后一个示例中,不会同时执行并行操作。它们将按预定义的顺序一一被“异步调用”

【讨论】:

  • 这一行:elem.recursion = await Promise.all(asyncInMap(elem.recursiveArray)); 让我意识到我做错了什么。没有等待递归调用的那些承诺。感谢您提供示例并花时间解释!
  • 添加此代码后:elem.recursion = await Promise.all(asyncInMap(elem.recursiveArray)); Third 不应该在所有 Second 日志都正确完成之前打印?从现在开始,我们正在等待递归调用的承诺。
  • 您应该在打印第三个之前等待。否则打印第三个是同步调用堆栈的一部分,并且不可能在异步代码之后执行同步代码
  • 如果你标记函数async,那么它会自动将返回值包装在Promise中,即使函数体是完全同步的。 js 中不可能从一段代码中调用异步代码并在该同步代码中返回流控制。只是异步方式
  • await Promise.all(arr.map(asyncFn)) - 这里的异步调用将是“并行的”,因为 .map 只会创建一个 promise 数组,而不需要对它们中的每一个进行 w8ing。 for(let item of arr) await asyncFn(item) - 在这里它将停止对每个项目的执行,并且会一直持续到项目的承诺得到解决,
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-08
  • 1970-01-01
  • 2022-06-19
  • 1970-01-01
  • 1970-01-01
  • 2020-09-13
相关资源
最近更新 更多