【问题标题】:Why will "await" break or kill a for-loop in modern Javascript?为什么“等待”会破坏或杀死现代 Javascript 中的 for 循环?
【发布时间】:2018-02-24 19:58:30
【问题描述】:

我有以下功能。它获取一个属性数组,并将在循环中迭代它们以将每个属性放入数据库中。但是循环会被await 语句自动终止:

async function importAttributeRecords(attributeValues, languageId, attributes, dataStorage, tx) {
  for(let attr of attributes) {
    console.log("Persist", attr)
    try {
      await importAttributeRecord(attributeValues, languageId, attr, dataStorage, tx) 
    }
    catch(err) {
      console.log(err);
    }
    console.log("After persisting");
  }
}

将执行对await 的第一次调用,但之后的第二个console.log 语句将永远不会出现。 另外,循环将立即退出。

即使返回一个承诺,我如何才能在循环中同步执行像 importAttributeRecord() 这样的函数? 为什么对循环使用“等待”很危险?

【问题讨论】:

  • 你的意思是第二个日志调用输出:"After persisting"?是否抛出异常?
  • 循环没有被杀死。它被搁置,并在您await 承诺时恢复。 Promise 是异步的。您不能将它们视为同步的。
  • importAttributeRecord返回一个承诺?你可能永远不会兑现这个承诺。
  • 如果没有importAttributeRecord 的代码,就不可能准确地说出你做错了什么,因为问题很可能出在那个函数上

标签: javascript ecmascript-6 promise async-await es6-promise


【解决方案1】:

这可以描述为“类别错误”。 for 循环基本上是一种同步编程思想,在(通常)一个数组或其他索引实体中做每一件事,一个接一个,每次都等待。

async 实现的异步编程是一种不同的方法。最完美的使用方式就是表达前后关系,让语言来处理时间,并可能并发或并行执行。

对于这种情况,您可以这样做:

async function importAttributeRecords(attributeValues, languageId, attributes, dataStorage, tx) {
  return Promise.all(attributes.map(attr => {
    console.log("Initiate persist", attr);
    return importAttributeRecord(attributesValues, languageId, attr, dataStorage, tx).then(result => {
      console.log("After persisting", attr, result);
    }).catch(err => {
      console.log("Error: ", attr, err);
    }));
  });
}

你会注意到 importAttributeRecords 现在返回一个 Promise 数组,所以它是合法的 async。您还会看到这段代码略短!

【讨论】:

  • 1.这个例子中没有任何东西等待来自importAttributeRecord 的承诺。你忘记Promise.all了吗? 2.问题中的代码没有问题。可能不建议在循环中使用await,因为它会串行运行,但这并不意味着它不起作用。任何破坏 OPs 代码的东西也会破坏你的代码。 for 循环本质上是串行,但本质上不是同步
  • @loganfsmyth 1. 谁说调用者不想使用Promise.race?你忘了那个可能性吗?此外,您是说您认为返回 .then 的结果不是惯用的并且与 async 兼容吗? 2. 你是说你认为 serial 是惯用的 asynchronous
  • "谁说这个调用者不想使用 Promise.race" 因为原始问题有一个循环,非常清楚地执行每个项目,等待它们全部。我的观点是return attributes.map( 返回一个承诺数组,无需等待其中任何一个完成,原始代码很明显只有在所有承诺完成后才从函数返回。 2. 在您的问题中,您说“for 循环从根本上说是一种同步编程思想”这是不对的,我的观点是循环定义了排序行为。代码可以是同步的或异步的,与循环使用无关。
  • @loganfsmyth 我认为您可能是对的,Promise.all 是最接近“for 循环”概念的惯用语。我将编辑我的答案,并感谢您对此的想法。我认为我的最后一条评论有点不必要的尖刻,我唯一的借口是我认为你最初的评论很尖刻(尽管我确信这不是你的意思)。另外,问题中缺少 MCVE 在某种程度上阻碍了我们在这里提供对未来最有用的答案。
猜你喜欢
  • 1970-01-01
  • 2021-03-14
  • 1970-01-01
  • 2012-05-20
  • 1970-01-01
  • 2020-10-04
  • 1970-01-01
  • 2015-09-10
  • 1970-01-01
相关资源
最近更新 更多