确保在另一个呼叫之前执行了与异步呼叫的foreach?

发布于 2025-01-21 14:22:24 字数 1001 浏览 3 评论 0原文

我具有多个功能,具有多个foreach loops:

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 loop中的异步代码调用this.addvertex函数是在执行下一个之前,确实要完成。

但是,当我记录变量时,似乎this.Addrelation函数在第一个循环真正结束之前被调用。

因此,我尝试在每个循环之前添加等待术语:

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);
      });
    });

但是相同的行为。

也许是日志函数具有延迟?有什么想法吗?

I have a function with multiple forEach loops:

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;
  }

I need to make sure that the async code in the forEach loop calling the this.addVertex function is really done before executing the next one.

But when I log variables, It seems that the this.addRelation function is called before the first loop is really over.

So I tried adding await terms before every loops like so :

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);
      });
    });

But same behavior.

Maybe it is the log function that have a latency? Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

长发绾君心 2025-01-28 14:22:24

正如我们已经讨论的那样,等待不会暂停.foreach()循环,并且不会使迭代的第二项等待处理第一个项目。因此,如果您真的想对项目进行异步测序,则无法真正使用.foreach()循环来完成它。

对于这种类型的问题,异步/等待与循环的普通非常有效,因为它们确实暂停了执行实际语句的实际对为您提供异步操作的测序,这是您想要的。另外,它甚至可以与循环的嵌套一起使用,因为它们都处于相同的函数范围:

向您展示它可以使用 and and 来使用多少等待,可以这样做:

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;
}

您可以编写类似同步的代码,实际上是在测序异步操作。


如果您确实避免了循环的任何,而您的实际要求只是所有调用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()放置在辅助功能中的Promise模式来手动对数组的同步访问序列:

// 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);
}

这将完全对所有内容进行序列。 It will do this type of order:

addVertex(annotation1)
addRelation(relation1);
addVertex(annotation2)
addRelation(relation2);
....
addVertex(annotationN);
addRelation(relationN);

where it waits for each operation to finish before going onto the next one.

As we've discussed, await does not pause a .forEach() loop and does not make the 2nd item of the iteration wait for the first item to be processed. So, if you're really trying to do asynchronous sequencing of items, you can't really accomplish it with a .forEach() loop.

For this type of problem, async/await works really well with a plain for loop because they do pause the execution of the actual for statement to give you sequencing of asynchronous operations which it appears is what you want. Plus, it even works with nested for loops because they are all in the same function scope:

To show you how much simpler this can be using for/of and await, it could be done like this:

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;
}

You get to write synchronous-like code that is actually sequencing asynchronous operations.


If you are really avoiding any for loop, and your real requirement is only that all calls to addVertex() come before any calls to addRelation(), then you can do this where you use .map() instead of .forEach() and you collect an array of promises that you then use Promise.all() to wait on the whole array of promises:

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);
}

To fully understand how this works, this runs all addVertex() calls in parallel for one annotation, waits for them all to finish, then runs all the addRelation() calls in parallel for one annotation, then waits for them all to finish. It runs all the annotations themselves in parallel. So, this isn't very much actual sequencing except within an annotation, but you accepted an answer that has this same sequencing and said it works so I show a little simpler version of this for completeness.


If you really need to sequence each individual addVertex() call so you don't call the next one until the previous one is done and you're still not going to use a for loop, then you can use the .reduce() promise pattern put into a helper function to manually sequence asynchronous access to an array:

// 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);
}

This will completely sequence everything. It will do this type of order:

addVertex(annotation1)
addRelation(relation1);
addVertex(annotation2)
addRelation(relation2);
....
addVertex(annotationN);
addRelation(relationN);

where it waits for each operation to finish before going onto the next one.

想念有你 2025-01-28 14:22:24

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;
}

foreach will return void so awaiting it will not do much. You can use map to return all the promises you create now in the forEach, and use Promise.all to await 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;
}
郁金香雨 2025-01-28 14:22:24

我了解您可以同时运行所有AddVertex。将简化与将MAP拆分成两套不同的承诺相结合。我的想法:

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);

您有更多的循环,但是它可以做到您所需要的

I understand you can run all the addVertex concurrently. Combining reduce with map splitted into two different set of promises you can do it. My idea:

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);

You have more loops, but it does what you need I think

没企图 2025-01-28 14:22:24

foreach对数组中的每个元素执行回调,并且不等待任何内容。使用等待基本上是写promise.then()的糖,并嵌套在然后()然后()呼叫中的所有内容。但是foreach不返回承诺,因此等待arr.foreach()是毫无意义的。它不是编译错误的唯一原因是因为异步/等待规格说您可以等待任何东西,如果不是保证,您只需获得其值... foreach foreach只给您void

如果您希望某些事情按顺序发生,则可以在循环中等待等待:

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

edit:在键入此几个其他答案和评论时。 ..您不想为循环使用,您可以使用Promise.All,但可能仍然有些混乱,因此,我将在上面的说明以防万一它有帮助。

forEach executes the callback against each element in the array and does not wait for anything. Using await is basically sugar for writing promise.then() and nesting everything that follows in the then() callback. But forEach doesn't return a promise, so await arr.forEach() is meaningless. The only reason it isn't a compile error is because the async/await spec says you can await anything, and if it isn't a promise you just get its value... forEach just gives you void.

If you want something to happen in sequence you can await in a for loop:

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

Edit: While typing this a couple other answers and comments happened... you don't want to use a for loop, you can use Promise.all but there's still maybe some confusion, so I'll leave the above explanation in case it helps.

英雄似剑 2025-01-28 14:22:24

异步/等待不在foreach中。

一个简单的解决方案:.foreach()将替换为(.. of ..)而替换。

类似的问题

如果启用了no-code弹性规则,则您将获得(..)使用的刺警告/错误。有很多讨论/观点 this主题。

恕我直言,这是一种场景,我们可以在其中抑制警告 eslint-disable-disable-next-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()获得类似的功能,我们需要一些我认为浪费精力的承诺/观察到的杂技。

async/await does not within forEach.

A simple solution: Replace .forEach() with for(.. of ..) instead.

Details in this similar question.

If no-iterator linting rule is enabled, you will get a linting warning/error for using for(.. of ..). There are lots of discussion/opinions on this topic.

IMHO, this is a scenario where we can suppress the warning with eslint-disable-next-line or for the method/class.

Example:

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
}

The code is very readable and works as expected. To get similar functionality with .forEach(), we need some promises/observables acrobatics that i think is a waste of effort.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文