Как мы уже говорили, await
не приостанавливает цикл .forEach()
и не заставляет 2-й элемент итерации ожидать обработки первого элемента. Итак, если вы действительно пытаетесь выполнить асинхронное упорядочение элементов, вы не сможете выполнить это с помощью цикла .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()
, чтобы не вызывать следующий, пока не будет завершен предыдущий, и вы все равно не собираетесь использовать цикл for
, тогда вы можете использовать .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);
}
Это полностью упорядочит все. Это сделает заказ такого типа:
addVertex(annotation1)
addRelation(relation1);
addVertex(annotation2)
addRelation(relation2);
....
addVertex(annotationN);
addRelation(relationN);
, где он ожидает завершения каждой операции, прежде чем перейти к следующей.