问题描述
这是我写的代码:
let aop = [
'https://api.coindesk.com/v1/bpi/currentprice.json','https://datausa.io/api/data?drilldowns=Nation&measures=Population','https://api.publicapis.org/entries'
];
(async function (){
Promise.all(aop.map(async url => {
let response = await fetch(url);
let json = await response.json();
return json;
})).then(x => console.log(x))
} ());
(function functionThatTakes30SecondsToFinish (){
// Some heavy lifting tasks
}())
我得到了我想要的东西,但我想知道它是如何工作的。
Promise.all
需要一组 promise,所以我使用 .map
来做到这一点。
现在让我困惑的事情是在第一次迭代中,对于第一个 url,fetch
将返回一个新的 Promise,然后遇到 await
关键字。由于函数调用的优先级高于 await
关键字,因此首先返回一个 promise。
我知道 await
会将您抛到当前执行上下文之外的一层,然后在 promise 得到解决后,收集其余代码并将其放入微任务队列以供执行。
但如果是这种情况,我们的第一个 promise 将被返回,并且我们将被抛到全局执行上下文之外的一层执行下一个函数,functionThatTakes30SecondsToFinish()
显然没有任何函数需要那么长时间才能完成,但只是为了举例,让我们假设一下。
一旦这个耗时的函数完成,我们是不是只处理了一个 URL?那也只能退了?
无论如何,假设 5 秒后我们收到了第一个响应,然后 await
将获取其下方的代码:
let json = await response.json();
return json;
...并将其放入微任务队列以供稍后执行。
如果这仅适用于一个 URL,那么我们如何再次返回 map
?
或者,这是我可以假设正在发生的第二件事:
map
将遍历每个 url,返回 promise,然后将所有这些代码都带入其内部隐藏数组,而不是将任何内容返回到最终返回的内部隐藏数组中:
let json = await response.json();
return json;
...并将其放入微任务队列以供稍后执行。
所以这就像 map
暂停每个元素的回调函数。
我很困惑,我什至不能正确地问这个。有人可以帮我解决这个问题吗?
解决方法
事实更接近你的第二个描述。
当一个 async
函数被执行时,它会在遇到 await
时返回(在评估它后面的表达式之后)。 .map
的行为是在第一个回调调用返回后,它将执行下一个元素的回调,等等。当它们遇到 await
时,它们中的每一个都会返回。所有这些都是同步发生的。
所以当 .map
完成所有回调后,它返回一个包含结果的数组。在这种情况下,结果都是承诺。
Promise.all
然后被执行,它创建了另一个承诺。 then
方法已在其上执行,但尚未执行其回调。
然后执行“繁重”代码。
一旦完成,并且调用堆栈为空,promise 作业队列将被监控。对于得到响应的 fetch
调用,该队列中将有一个作业。它甚至可能是 3 个作业,如果在那个繁忙的时期,所有三个请求的 HTTP 响应都到达了。
这些作业对应于带有 await
的已解决承诺。具有 await
的函数的执行上下文将被恢复,并继续执行,等等。如果没有更多要执行的内容,则将处理该队列中的下一个作业,从而导致类似的情况,等等。
由于有更多的 awaits
(伴随着 .json()
调用),该函数将再次返回,这一次使调用堆栈为空,这将再次导致一个 promise 作业被添加到队列(当 .json()
完成其工作时)。
当所有这些带有 await
的 promise 都解决了,并且执行之后的所有代码时,传递给 Promise.all
的 promise 都会解决。这将依次解析 Promise.all
创建的承诺,因此链接的 then
回调将执行。