我有一个包含多个forEach
循环的函数:
async insertKpbDocument(jsonFile) {
jsonFile.doc.annotations.forEach((annotation) => {
annotation.entities.forEach(async (entity) => {
await this.addVertex(entity);
});
annotation.relations.forEach(async (relation) => {
await this.addRelation(relation);
});
});
return jsonFile;
}
我需要确保调用forEach
函数的this.addVertex
循环中的异步代码在执行下一个函数之前已经完成。
但是当我记录变量时,似乎在第一个循环真正结束之前调用this.addRelation
函数。
所以我尝试在每个循环之前添加await
个术语,如下所示:
await jsonFile.doc.annotations.forEach(async (annotation) => {
await annotation.entities.forEach(async (entity) => {
await this.addVertex(entity);
});
await annotation.relations.forEach(async (relation) => {
await this.addRelation(relation);
});
});
但同样的行为。
也许是日志函数有延迟?有什么想法吗?
答案 0 :(得分:5)
foreach
将返回void
,等待它不会做太多。您可以使用map
返回您在forEach
中创建的所有承诺,并使用Promise.all
等待所有:
async insertKpbDocument(jsonFile: { doc: { annotations: Array<{ entities: Array<{}>, relations: Array<{}> }> } }) {
await Promise.all(jsonFile.doc.annotations.map(async(annotation) => {
await Promise.all(annotation.entities.map(async (entity) => {
await this.addVertex(entity);
}));
await Promise.all(annotation.relations.map(async (relation) => {
await this.addRelation(relation);
}));
}));
return jsonFile;
}
答案 1 :(得分:3)
正如我们已经讨论的那样,await
不会暂停.forEach()
循环,也不会使迭代的第二项等待处理第一项。因此,如果您真的尝试对项目进行异步排序,则无法通过.forEach()
循环实现此目的。
对于这类问题,async/await
在普通for
循环中工作得很好,因为它们会暂停执行实际的for
语句,以便为您提供异步操作的排序出现就是你想要的。此外,它甚至适用于嵌套的for
循环,因为它们都在相同的函数范围内:
为了向您展示使用for/of
和await
的简单程度,可以这样做:
async insertKpbDocument(jsonFile) {
for (let annotation of jsonFile.doc.annotations) {
for (let entity of annotation.entities) {
await this.addVertex(entity);
}
for (let relation of annotation.relations) {
await this.addRelation(relation);
}
}
return jsonFile;
}
您可以编写类似于同步的代码,实际上是对异步操作进行排序。
如果您真的避免任何for
循环,并且您的真正要求只是所有对addVertex()
的调用都来自对addRelation()
的任何调用,那么您可以在您使用的地方执行此操作.map()
而不是.forEach()
并且您收集了一系列承诺,然后使用Promise.all()
等待整个承诺数组:
insertKpbDocument(jsonFile) {
return Promise.all(jsonFile.doc.annotations.map(async annotation => {
await Promise.all(annotation.entities.map(entity => this.addVertex(entity)));
await Promise.all(annotation.relations.map(relation => this.addRelation(relation)));
})).then(() => jsonFile);
}
要完全理解这是如何工作的,这将为一个注释并行运行所有addVertex()
个调用,等待所有注释完成,然后并行运行所有addRelation()
个调用以进行一个注释,然后等待为他们所有人完成。它并行运行所有注释。因此,除了注释之外,这不是非常实际的排序,但是您接受了具有相同排序的答案,并说它有效,所以为了完整性,我展示了一个更简单的版本。
如果您确实需要对每个addVertex()
个人进行排序,那么您就不要打电话给下一个,直到上一个完成并且您仍然不会使用{{1}然后你可以使用放入帮助函数的for
promise模式手动排序对数组的异步访问:
.reduce()
这将完全排序一切。它将执行此类订单:
// helper function to sequence asynchronous iteration of an array
// fn returns a promise and is passed an array item as an argument
function sequence(array, fn) {
return array.reduce((p, item) => {
return p.then(() => {
return fn(item);
});
}, Promise.resolve());
}
insertKpbDocument(jsonFile) {
return sequence(jsonFile.doc.annotations, async (annotation) => {
await sequence(annotation.entities, entity => this.addVertex(entity));
await sequence(annotation.relations, relation => this.addRelation(relation));
}).then(() => jsonFile);
}
等待每个操作完成后再进入下一个操作。
答案 2 :(得分:0)
据我所知,您可以同时运行所有addVertex。结合使用reduce和map分成两组不同的promises,你可以做到。我的想法:
const first = jsonFile.doc.annotations.reduce((acc, annotation) => {
acc = acc.concat(annotation.entities.map(this.addVertex));
return acc;
}, []);
await Promise.all(first);
const second = jsonFile.doc.annotations.reduce((acc, annotation) => {
acc = acc.concat(annotation.relations.map(this.addRelation));
return acc;
}, []);
await Promise.all(second);
你有更多的循环,但它可以满足我的需求
答案 3 :(得分:0)
async/await
不在forEach
内。
一个简单的解决方案:将.forEach()
替换为for(.. of ..)
。
此similar question中的详细信息。
如果启用了no-iterator
linting规则,则使用for(.. of ..)
会收到linting警告/错误。这个主题有很多discussion/opinions。
恕我直言,我们可以使用eslint-disable-next-line或方法/类抑制警告。
示例:
const insertKpbDocument = async (jsonFile) => {
// eslint-disable-next-line no-iterator
for (let entity of annotation.entities) {
await this.addVertex(entity)
}
// eslint-disable-next-line no-iterator
for (let relation of annotation.relations) {
await this.addRelation(relation)
}
return jsonFile
}
代码非常可读并按预期工作。为了获得与.forEach()
类似的功能,我们需要一些承诺/可观察的杂技,我认为这是浪费精力。
答案 4 :(得分:0)
forEach
对数组中的每个元素执行回调,不等待任何事情。使用await
基本上是用于编写promise.then()
的糖,并在then()
回调中嵌套后面的所有内容。但forEach
并未返回承诺,因此await arr.forEach()
毫无意义。它不是编译错误的唯一原因是因为async / await规范说你可以await
任何东西,如果它不是一个承诺你只是得到它的价值...... forEach
只是给出你void
。
如果您希望按顺序发生某些事情,可以在await
循环中for
:
for (let i = 0; i < jsonFile.doc.annotations.length; i++) {
const annotation = jsonFile.doc.annotations[i];
for (let j = 0; j < annotation.entities.length; j++) {
const entity = annotation.entities[j];
await this.addVertex(entity);
});
// code here executes after all vertix have been added in order
编辑:在输入其他几个答案和评论时...您不想使用for
循环,您可以使用Promise.all
但是有仍然可能有些混乱,所以我会留下上面的解释以防万一。