Abd*_*led 7 javascript node.js promise async-await
在类似问题的接受答案中,答案指出forEach调用只是抛出一个承诺然后退出。forEach我认为返回应该是这种情况undefined,但是为什么下面的代码可以工作?
const networkFunction = (callback) => {\n return new Promise((resolve) => {\n setTimeout(() => {\n resolve(callback());\n }, 200);\n });\n};\n\n(async () => {\n const numbers = [0, 1, 2];\n // works in parallel\n numbers.forEach(async (num) => {\n await networkFunction(() => {\n console.log("For Each Function: Hello");\n });\n });\n})();\nRun Code Online (Sandbox Code Playgroud)\n它并行工作,这是输出time node main.js # main.js contains only the mentioned code
\xe2\x9d\xaf time node main.js\nFor Each Function: Hello\nFor Each Function: Hello\nFor Each Function: Hello\n\n________________________________________________________\nExecuted in 365.63 millis fish external\n usr time 126.02 millis 964.00 micros 125.05 millis\n sys time 36.68 millis 618.00 micros 36.06 millis\nRun Code Online (Sandbox Code Playgroud)\n
Sco*_*son 37
OP 的问题是要求对此处找到的另一个 StackOverflow 问题进行澄清。如需进一步阅读以及有关此一般主题的许多其他精彩答案,请查看链接。
不要将 async/await 与 forEach 一起使用。要么使用for-of循环,要么使用Promise.all()with array.map()。
promise.all()如果您对 Promise 和 async/await 有一个大致的了解,那么关于+array.map()和,之间的区别的 TL;DR.forEach()是不可能等待 aforEach()本身。.forEach()是的,您可以像使用 一样并行运行任务.map(),但您不能等待所有这些并行任务完成,然后在它们全部完成后执行某些操作。.map()使用而不是 的全部意义.forEach()在于,您可以获得承诺列表,使用 收集它们Promise.all(),然后等待整个过程。要明白我的意思,只需在console.log('Finished')a 之后放置 a forEach(async () => ...),您就会看到在循环"finished"中的所有内容完成运行之前就已注销.forEach()。我的建议是不要使用异步逻辑(实际上现在.forEach()没有理由再使用,正如我在下面进一步解释的那样)。.forEach()
对于那些需要更深入的东西的人,这个答案的其余部分将深入更详细的内容,首先对承诺进行一个小回顾,然后深入解释这些方法的行为有何不同以及为什么总是.forEach()较差的解决方案当谈到异步/等待时。
出于本次讨论的目的,您只需记住承诺是一个特殊的对象,它承诺某些任务将在未来的某个时刻完成。您可以通过 将侦听器附加到 Promise .then(),以便在任务完成时收到通知并接收解析值。
函数async就是一个无论如何都会返回一个 Promise 的函数。即使你这样做async function doThing() { return 2 },它也不会返回 2,它将返回一个立即解析为值 2 的 Promise。请注意,异步函数将始终立即返回 Promise,即使该函数需要很长时间才能运行。这就是为什么它被称为“承诺”,它承诺该函数最终将完成运行,如果您想在函数完成时收到通知,您可以通过 或 向其添加事件侦听.then()器await。
await是一种特殊的语法,可让您暂停异步函数的执行,直到承诺解决。await只会影响其直接内部的功能。在幕后,await只需向 Promise 添加一个特殊的事件监听器.then(),这样它就可以知道 Promise 何时解析以及解析的值是什么。
async function fn1() {
async function fn2() {
await myPromise // This pauses execution of fn2(), not fn1()!
}
...
}
async function fn1() {
function fn2() {
await myPromise // An error, because fn2() is not async.
}
...
}
Run Code Online (Sandbox Code Playgroud)
如果您能够很好地掌握这些原则,那么您应该能够理解接下来的部分。
for-of循环for-of允许您一个接一个地串行执行异步任务。例如:
const delays = [1000, 1400, 1200];
// A function that will return a
// promise that resolves after the specified
// amount of time.
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))
async function main() {
console.log('start')
for (const delay of delays) {
await wait(delay)
console.log('Finished waiting for the delay ' + delay)
}
console.log('finish')
}
main()Run Code Online (Sandbox Code Playgroud)
导致暂停指定的延迟,之后循环继续,执行await,循环再次开始下一次迭代,开始新的延迟。main()console.log()
希望这个应该有点简单。
Promise.all()+array.map()一起使用Promise.all()和array.map()可以让您有效地并行运行许多异步任务,例如,我们可以等待许多不同的延迟同时完成。
const delays = [1000, 1400, 1200];
// A function that will return a
// promise that resolves after the specified
// amount of time.
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))
async function main() {
console.log('start')
await Promise.all(delays.map(async delay => {
await wait(delay)
console.log('Finished waiting for the delay ' + delay)
}))
console.log('finish')
}
main()Run Code Online (Sandbox Code Playgroud)
如果您还记得我们对 Promise 和 async/await 的快速入门,您会记得它await只会影响它直接位于内部的函数,导致该函数暂停。在这种情况下,awaitfromawait wait(delay)不会main()像前面的示例中那样导致暂停,相反,它将导致传入的回调delays.map()暂停,因为这是它直接位于内部的函数。
因此,我们delays.map()将调用所提供的回调,为数组delay内部的每个内部调用一次delays。回调是异步的,因此它总是会立即返回一个承诺。回调将开始使用不同的延迟参数执行,但不可避免地会中断await wait(delay),暂停回调的执行。
.map()因为返回一个 Promise 的回调delays.map()将返回一组 Promise,Promise.all()然后该数组将接收它们,并将它们组合成一个超级 Promise,当数组中的所有 Promise 都解析时,该超级 Promise 才会解析。现在,我们await返回了 的超级承诺promise.all()。这await是 inside main(),导致main()暂停,直到所有提供的承诺都解决。因此,我们在 内部创建了一堆独立的异步任务main(),让它们都在自己的时间完成,然后暂停main()自身的执行,直到所有这些任务都完成。
首先,你真的不需要使用forEach()任何东西。它在循环出现之前就出现了for-of,而且在各个方面for-of都比它好。可以针对任何迭代运行,您可以使用and与它们一起使用,而且,对于本主题来说最重要的是,它将按预期工作,但在. 原因如下:forEach()for-ofbreakcontinueawaitfor-offorEach()
const delays = [1000, 1400, 1200];
// A function that will return a
// promise that resolves after the specified
// amount of time.
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))
async function main() {
console.log('start')
delays.forEach(async delay => {
await wait(delay)
console.log('Finished waiting for the delay ' + delay)
})
console.log('finish')
}
main()Run Code Online (Sandbox Code Playgroud)
首先,您会注意到这.forEach()将导致任务并行运行而不是串行运行,就像.map(). 这是因为await的内部forEach()仅影响回调,而不影响main()。因此,当我们运行时,我们为每个indelays.forEach()调用这个异步函数,启动一堆异步任务。问题是,没有什么会等待异步任务完成,实际上,不可能等待它们。异步回调每次被调用时都会返回 Promise,但与 不同的是,它完全忽略其回调的返回值。收到承诺,然后简单地忽略它。这使得不可能像我们之前那样将承诺组合在一起并全部通过。因此,您会注意到立即注销,因为我们永远不会等待这些承诺完成。这可能不是您想要的。delaydelays.map().forEach().forEach()awaitPromise.all()"finish"main()
(又名原始答案)
它之所以有效,是因为 for 循环仍然运行,并且您仍然启动一堆异步任务。问题是你没有在等他们。当然,您在 内 使用了await .forEach(),但这只会导致.forEach()回调等待,而不会暂停您的外部函数。console.log()如果您在 async IIFE 末尾添加 a ,您可以看到这一点,您的 console.log() 将在所有请求完成之前立即触发。如果您使用 Promise.all() 代替,那么 console.log() 将在请求完成后触发。
破损版本:
const networkFunction = (callback) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(callback());
}, 200);
});
};
(async () => {
const numbers = [0, 1, 2];
// works in parallel
numbers.forEach(async (num) => {
await networkFunction(() => {
console.log("For Each Function: Hello");
});
});
console.log('All requests finished!')
})();Run Code Online (Sandbox Code Playgroud)
固定版本:
const networkFunction = (callback) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(callback());
}, 200);
});
};
(async () => {
const numbers = [0, 1, 2];
// works in parallel
await Promise.all(numbers.map(async (num) => {
await networkFunction(() => {
console.log("For Each Function: Hello");
});
}));
console.log('All requests finished!')
})();Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
13043 次 |
| 最近记录: |