ES7 async/await 概念问题
ES7 async/await conceptual issue
我正在迁移现有程序以使用 async
/await
(通过 Babel
的 bluebirdCoroutines
)以学习这种风格。我一直在看这个 tutorial.
我对以下行为有点困扰。此代码段按预期工作:
let parts = [];
let urlsP = urls.map((url, index) => {
return dlPart(url, index, tempDir);
});
for (let urlP of urlsP) { // Parallel (yay!)
parts.push(await urlP);
}
for (let part of parts) { // Sequential
await appendFile(leFile, part);
}
重写如下,仍然有效,但第一个操作不再并行(需要更长的时间才能完成)!
let index = 0;
let parts = [];
for (let url of urls) { // NOT Parallel any more!!!
index++;
parts.push(await dlPart(url, index, tempDir));
}
for (let part of parts) {
await appendFile(leFile, part);
}
这是dlPart()
的实现
function dlPart(url, num, dir) {
var cmd = 'wget --quiet "' + url + '" -O ' + dir + "/" + num;
return exec(cmd).then(() => {
return dir + '/' + num;
});
}
我错过了什么?
.map
函数是异步的,因此您的其余代码不必等待它,它会在准备就绪时完成。然后你用 for loop
替换它,它在完成时阻止所有内容。
它不再 运行 并行的原因是因为您在两个示例中创建承诺的时间。这个描述得更清楚。
在您的第一个示例中,您启动了所有开始执行其功能的承诺。然后在这个循环中:
for (let urlP of urlsP) { // Parallel (yay!)
parts.push(await urlP);
}
您等待第一个 promise 完成,然后第二个 promise 完成,等等。但是在您等待第一个 promise 完成的整个过程中,所有其他 promise 仍在执行。因此他们 运行 在 "parallel".
在您的第二个示例中,您在循环内启动和等待承诺,而不是在循环之前启动它们。所以在这段代码中:
for (let url of urls) { // NOT Parallel any more!!!
index++;
parts.push(await dlPart(url, index, tempDir));
}
parts.push
行按顺序执行以下操作:
- 运行
dlPart()
returns 承诺并开始下载部分
- 等待承诺解决
- 将解析值推入
parts
。
所有其他 promise 还没有开始,也没有 运行ning "in parallel" 它们只有在轮到他们进入循环时才开始。这意味着他们一次被调用一个,只有在前一个完成后才开始执行下一个,这就是为什么他们 运行 迭代。
注意:.map
is not asynchronous 如果是,那么您的第一个示例将不适用于大型列表,因为 for of
循环将在所有承诺添加到您的 urlsP
数组之前开始.
当代码以稍微不同的方式编写时,您可以更好地看到代码之间的差异。
出于所有意图和目的,这正是 Sam 所解释的内容,但我发现以一种他们更习惯的方式帮助开发人员理解它。
ES7代码
let parts = [];
let urlsP = urls.map((url, index) => {
return dlPart(url, index, tempDir);
});
for (let urlP of urlsP) { // Parallel (yay!)
parts.push(await urlP);
}
ES6代码
let parts = [];
// Capture all the pending promises into an array
let urlsP = urls.map((url,index)=>{
// Returns the promise to the urlsP array
return dlPart(url,index,tempDir);
});
// Catch all the data in an array
Promise.all(urlsP).then(res=>{
parts=res;
});
重申 Sam 在上面 post 中的解释。
在 ES7 示例中,map 函数调用所有异步函数并创建一个新的承诺数组。 for of loop
遍历承诺数组并检查承诺是否已解决,如果尚未解决,它将等到特定承诺解决,然后重复此过程。如果您能够使用 chrome 中的调试器标签以慢动作观看此代码,您会注意到某些承诺将在循环检查它是否已解决时得到解决,而其他承诺则必须等待对于
ES6 示例本质上是相同的,唯一的区别在于我们如何获取 parts 数组。在这种情况下,Promise all 响应是所有解析值的数组,因此我们使部分等于响应而不是推入数组
现在想象一下在 es6 中编写以下代码:
ES7代码
let index = 0; let parts = [];
for (let url of urls) { // NOT Parallel any more!!!
index++;
parts.push(await dlPart(url, index, tempDir));
}
你必须使用生成器或递归函数,我对生成器的理解还很新,所以我将展示一个递归函数
ES5/6代码
let index = 0; let parts = []; let promises = [];
function getParts(index,){
return new Promise((resolve,reject)=>{
dlPart(urls[index],index,tempDir).then(res=>{
parts.push(res)
if(index<urls.length-1){
promises.push(getParts(++index));
resolve()
}else{
resolve()
}
}
}
}
promises.push(getParts(index));
Promise.all(promises).then(finished=>{
// Then you can continue with whatever code
});
现在使用上面的 ES7 示例代码,您会注意到 for of loop
遍历 urls 数组并等待在移动到数组的下一个索引之前解决的承诺。
ES6 示例在某种意义上做同样的事情,它将从索引 0 处的 url 开始,等待 dlPart
promise to resolve,将响应推送到 parts 数组,检查索引是否仍然小于 urls 数组长度,然后 getParts 再次调用自身,直到最后 运行s out of urls 索引并解决其最后的承诺,以便 Promise.all(promises)
下面的代码可以开始 运行
当您开始查看 ES6 和 ES7 之间的可读性差异时,您会明白为什么 async/await 在 es7 规范中最终确定。
我正在迁移现有程序以使用 async
/await
(通过 Babel
的 bluebirdCoroutines
)以学习这种风格。我一直在看这个 tutorial.
我对以下行为有点困扰。此代码段按预期工作:
let parts = [];
let urlsP = urls.map((url, index) => {
return dlPart(url, index, tempDir);
});
for (let urlP of urlsP) { // Parallel (yay!)
parts.push(await urlP);
}
for (let part of parts) { // Sequential
await appendFile(leFile, part);
}
重写如下,仍然有效,但第一个操作不再并行(需要更长的时间才能完成)!
let index = 0;
let parts = [];
for (let url of urls) { // NOT Parallel any more!!!
index++;
parts.push(await dlPart(url, index, tempDir));
}
for (let part of parts) {
await appendFile(leFile, part);
}
这是dlPart()
function dlPart(url, num, dir) {
var cmd = 'wget --quiet "' + url + '" -O ' + dir + "/" + num;
return exec(cmd).then(() => {
return dir + '/' + num;
});
}
我错过了什么?
.map
函数是异步的,因此您的其余代码不必等待它,它会在准备就绪时完成。然后你用 for loop
替换它,它在完成时阻止所有内容。
它不再 运行 并行的原因是因为您在两个示例中创建承诺的时间。这个描述得更清楚
在您的第一个示例中,您启动了所有开始执行其功能的承诺。然后在这个循环中:
for (let urlP of urlsP) { // Parallel (yay!)
parts.push(await urlP);
}
您等待第一个 promise 完成,然后第二个 promise 完成,等等。但是在您等待第一个 promise 完成的整个过程中,所有其他 promise 仍在执行。因此他们 运行 在 "parallel".
在您的第二个示例中,您在循环内启动和等待承诺,而不是在循环之前启动它们。所以在这段代码中:
for (let url of urls) { // NOT Parallel any more!!!
index++;
parts.push(await dlPart(url, index, tempDir));
}
parts.push
行按顺序执行以下操作:
- 运行
dlPart()
returns 承诺并开始下载部分 - 等待承诺解决
- 将解析值推入
parts
。
所有其他 promise 还没有开始,也没有 运行ning "in parallel" 它们只有在轮到他们进入循环时才开始。这意味着他们一次被调用一个,只有在前一个完成后才开始执行下一个,这就是为什么他们 运行 迭代。
注意:.map
is not asynchronous 如果是,那么您的第一个示例将不适用于大型列表,因为 for of
循环将在所有承诺添加到您的 urlsP
数组之前开始.
当代码以稍微不同的方式编写时,您可以更好地看到代码之间的差异。
出于所有意图和目的,这正是 Sam 所解释的内容,但我发现以一种他们更习惯的方式帮助开发人员理解它。
ES7代码
let parts = [];
let urlsP = urls.map((url, index) => {
return dlPart(url, index, tempDir);
});
for (let urlP of urlsP) { // Parallel (yay!)
parts.push(await urlP);
}
ES6代码
let parts = [];
// Capture all the pending promises into an array
let urlsP = urls.map((url,index)=>{
// Returns the promise to the urlsP array
return dlPart(url,index,tempDir);
});
// Catch all the data in an array
Promise.all(urlsP).then(res=>{
parts=res;
});
重申 Sam 在上面 post 中的解释。
在 ES7 示例中,map 函数调用所有异步函数并创建一个新的承诺数组。 for of loop
遍历承诺数组并检查承诺是否已解决,如果尚未解决,它将等到特定承诺解决,然后重复此过程。如果您能够使用 chrome 中的调试器标签以慢动作观看此代码,您会注意到某些承诺将在循环检查它是否已解决时得到解决,而其他承诺则必须等待对于
ES6 示例本质上是相同的,唯一的区别在于我们如何获取 parts 数组。在这种情况下,Promise all 响应是所有解析值的数组,因此我们使部分等于响应而不是推入数组
现在想象一下在 es6 中编写以下代码:
ES7代码
let index = 0; let parts = [];
for (let url of urls) { // NOT Parallel any more!!!
index++;
parts.push(await dlPart(url, index, tempDir));
}
你必须使用生成器或递归函数,我对生成器的理解还很新,所以我将展示一个递归函数
ES5/6代码
let index = 0; let parts = []; let promises = [];
function getParts(index,){
return new Promise((resolve,reject)=>{
dlPart(urls[index],index,tempDir).then(res=>{
parts.push(res)
if(index<urls.length-1){
promises.push(getParts(++index));
resolve()
}else{
resolve()
}
}
}
}
promises.push(getParts(index));
Promise.all(promises).then(finished=>{
// Then you can continue with whatever code
});
现在使用上面的 ES7 示例代码,您会注意到 for of loop
遍历 urls 数组并等待在移动到数组的下一个索引之前解决的承诺。
ES6 示例在某种意义上做同样的事情,它将从索引 0 处的 url 开始,等待 dlPart
promise to resolve,将响应推送到 parts 数组,检查索引是否仍然小于 urls 数组长度,然后 getParts 再次调用自身,直到最后 运行s out of urls 索引并解决其最后的承诺,以便 Promise.all(promises)
下面的代码可以开始 运行
当您开始查看 ES6 和 ES7 之间的可读性差异时,您会明白为什么 async/await 在 es7 规范中最终确定。