JavaScript异步和循环等待
基本的异步
和等待
很简单。当您尝试使用时,事情变得更加复杂等待
在循环中。
在本文中,我想分享一些陷阱,以防您打算使用等待
在循环中。
在你开始之前
我要假设你知道如何使用异步
和等待
。如果您不这样做,请阅读上一篇文章在继续之前熟悉自己。
准备一个例子
在本文中,假设您想从水果篮中获取水果的数量。
const frualbasket = {
苹果:27,
葡萄:0,
梨:14
};
您想从水果酱中获取每种水果的数量。要获得水果的数量,您可以使用getnumfruit
功能。
const getnumfruit = fruit => {
返回水果酱[水果];
};const numapples = getnumfruit(“苹果”);
console.log(numapples);// 27
现在,假设水果篮
生活在远程服务器上。访问它需要一秒钟。我们可以通过超时嘲笑这个一秒钟的延迟。(请参考上一篇文章如果您有理解超时代码的问题)。
const sleep = ms => {
返回新的Promise(Resolve => settimeout(Resolve,MS));
};const getnumfruit = fruit => {
返回睡眠(1000)。
};getnumfruit(“苹果”)。然后(num => console.log(num));// 27
最后,假设您想使用等待
和getnumfruit
要在异步功能中获取每个果实的数量。
const Control = async _ => {
console.log(“ start”);const numapples =等待getnumfruit(“苹果”);
console.log(numapples);const numgrapes =等待getnumfruit(“葡萄”);
console.log(numgrapes);const numpears =等待getnumfruit(“梨”);
console.log(numpears);console.log(“ end”);
};
这样,我们可以开始看等待
在循环中。
等待循环
假设我们有一系列我们想从水果篮中获得的水果。
const fruitstoget = [“苹果”,“葡萄”,“梨”];
我们将循环通过此数组。
const forloop = async _ => {
console.log(“ start”);for(令index = 0; index//得到每种水果的数量 console.log(“ end”);
}
};
在循环中,我们将使用getnumfruit
获取每个水果的数量。我们还将将数字记录到控制台中。
自从getnumfruit
回报诺言,我们可以等待
记录之前已解决的值。
const forloop = async _ => {
console.log(“ start”);for(令index = 0; indexconst fruit = fruitstoget [index]; console.log(“ end”);
const numfruit =等待getnumfruit(水果);
console.log(numfruit);
}
};
当您使用时等待
,您希望JavaScript暂停执行,直到已期待的承诺得到解决为止。这表示等待
循环中的s应串联执行。
结果就是您所期望的。
“开始”;
“苹果:27”;
“葡萄:0”;
“梨:14”;
“结尾”;
这种行为与大多数循环一起起作用(例如尽管
和对于
循环)...
但是它不适用于需要回调的循环。需要后备的循环的示例包括foreach
,,,,地图
,,,,筛选
, 和减少
。我们将看看如何等待
影响foreach
,,,,地图
, 和筛选
在接下来的几节中。
等待foreach循环
我们将做与前循环示例中相同的事情。首先,让我们循环通过一系列水果。
const foreachloop = _ => {
console.log(“ start”);fruitstoget.foreach(水果=> {
//向每种水果发送承诺
});console.log(“ end”);
};
接下来,我们将尝试获得水果的数量getnumfruit
。(注意异步
回调函数中的关键字。我们需要这个异步
关键字是因为等待
在回调函数中)。
const foreachloop = _ => {
console.log(“ start”);fruitstoget.foreach(异步水果=> {
const numfruit =等待getnumfruit(水果);
console.log(numfruit);
});console.log(“ end”);
};
您可能希望控制台看起来像这样:
“开始”;
“ 27”;
“ 0”;
“ 14”;
“结尾”;
但是实际结果是不同的。JavaScript继续致电console.log('end')
在解决方案中的承诺得到解决之前。
控制台按以下顺序记录:
'开始'
'结尾'
‘27’
‘0'
‘14’
JavaScript这样做是因为foreach
不是承诺意识。它不能支持异步
和等待
。你_不能_采用等待
在foreach
。
等待地图
如果您使用等待
在一个地图
,,,,地图
将始终归还一系列承诺。这是因为异步函数总是返回承诺。
const maploop = async _ => {
console.log(“ start”);const numfruits =等待fruitstoget.map(async fruit => {
const numfruit =等待getnumfruit(水果);
返回numfruit;
});console.log(numfruits);console.log(“ end”);
};“开始”;
“ [承诺,应许,承诺]”;
“结尾”;
自从地图
始终返回承诺(如果您使用等待
),您必须等待一系列的诺言才能解决。你可以做到这一点等待Promise.All(ArrayofPromises)
。
const maploop = async _ => {
console.log(“ start”);const承诺= fruitstoget.map(async fruit => {
const numfruit =等待getnumfruit(水果);
返回numfruit;
});const numfruits =等待Promise.All(承诺);
console.log(numfruits);console.log(“ end”);
};
这是您得到的:
“开始”;
“ [27,0,14]”;
“结尾”;
如果您愿意,您可以操纵您在承诺中返回的价值。解决的值将是您返回的值。
const maploop = async _ => {
//…
const承诺= fruitstoget.map(async fruit => {
const numfruit =等待getnumfruit(水果);
//返回之前添加onn水果
返回numfruit + 100;
});
//…
};“开始”;
“ [127,100,114]”;
“结尾”;
等待过滤器
当您使用时筛选
,您想过滤一个具有特定结果的数组。假设您想创建一个具有20多个水果的阵列。
如果您使用筛选
通常(不等待),您会这样使用:
//过滤如果没有等待
const filterloop = _ => {
console.log(“开始”)const morethan20 =等待fruitstoget.filter(fruit => {
const numfruit = fruitbasket [水果]
返回numfruit> 20
})console.log(Morethan20)
console.log('end')
}
您会期望的超过20
仅包含苹果,因为有27个苹果,但是有0个葡萄和14个梨。
“开始” [“苹果”];
(“结尾”);
等待
在筛选
无法以相同的方式工作。实际上,它根本不起作用。您会得到未经过滤的阵列...
const filterloop = _ => {
console.log(“开始”)const morethan20 =等待fruitstoget.filter(异步水果=> {
const numfruit = getnumfruit(水果)
返回numfruit> 20
})console.log(Morethan20)
console.log('end')
}“开始” [(“苹果”,“葡萄”,“梨”)];
(“结尾”);
这就是为什么它发生的原因。
当您使用时等待
在一个筛选
回调,回调总是一个承诺。由于承诺总是真实的,因此阵列中的所有内容都通过过滤器。写作等待
在一个筛选
就像编写此代码:
//一切都通过过滤器…
const filled = array.filter(true);
有三个步骤要使用等待
和筛选
适当地:
1.使用地图
返回阵列承诺
2。等待
诺言阵列
3。筛选
解决值
const filterloop = async _ => {
console.log(“ start”);const承诺=等待fruitstoget.map(水果=> getnumfruit(果实));
const numfruits =等待Promise.All(承诺);const morethan20 = fruitstoget.filter(((水果,索引)=> {
const numfruit = numfruits [index];
返回numfruit> 20;
});Console.Log(Morethan20);
console.log(“ end”);
};启动[“苹果”];
结尾;
等待减少
对于这种情况,假设您想找出水果河中的水果总数。通常,您可以使用减少
循环浏览数组并总结数字。
//减少如果没有等待
const reduceloop = _ => {
console.log(“ start”);const sum = fruitstoget.dreduce((sum,fruit)=> {
const numfruit = fruitbasket [frual];
返回sum + numfruit;
},0);console.log(sum);
console.log(“ end”);
};
您将总共获得41个水果。(27 + 0 + 14 = 41)。
“开始”;
“ 41”;
“结尾”;
当您使用时等待
随着减少,结果变得非常混乱。
//减少如果我们等待getnumfruit
const reduceloop = async _ => {
console.log(“ start”);const sum =等待fruitstoget.dreduce(async(sum,fruit)=> {
const numfruit =等待getnumfruit(水果);
返回sum + numfruit;
},0);console.log(sum);
console.log(“ end”);
};“开始”;
“ [对象承诺] 14”;
“结尾”;
什么?![对象承诺] 14
?!
解剖这很有趣。
- 在第一次迭代中,
和
是0
。Numfruit
是27(从getnumfruit(“苹果”)
)。0 + 27
是27。 - 在第二次迭代中,
和
是诺言。(为什么?因为异步函数总是返回承诺!)Numfruit
是0。不能正常将承诺添加到一个对象,因此JavaScript将其转换为[对象承诺]
细绳。[对象承诺] + 0
是[对象承诺] 0
- 在第三次迭代中,
和
也是一个诺言。Numfruit
是14
。[对象承诺] + 14
是[对象承诺] 14
。
谜团已揭开!
这意味着您可以使用等待
在一个减少
回调,但您必须记住等待
蓄能器首先!
const reduceloop = async _ => {
console.log(“ start”);const sum =等待fruitstoget.dreduce(async(承诺,水果)=>
const sum =等待承诺;
const numfruit =等待getnumfruit(水果);
返回sum + numfruit;
},0);console.log(sum);
console.log(“ end”);
};“开始”;
“ 41”;
“结尾”;
但是...从GIF中可以看到,花费很长时间等待
一切。发生这种情况是因为还原
需要等待应许
为每次迭代完成。
有一种方法可以加快减少循环的速度。(我发现了这一点蒂姆·奥克斯利。如果你等待getnumfruits(
)首先等待承诺
, 这还原
仅需一秒钟即可完成:
const reduceloop = async _ => {
console.log(“ start”);const sum =等待fruitstoget.dreduce(async(承诺,水果)=>
//重量首先。
//这触发了所有三个getnumfruit承诺,然后再等待循环的下一次迭代。
const numfruit =等待getnumfruit(水果);
const sum =等待承诺;
返回sum + numfruit;
},0);console.log(sum);
console.log(“ end”);
};
这是因为减少
可以解雇这三个getnumfruit
在等待循环的下一次迭代之前承诺。但是,这种方法有点令人困惑,因为您必须小心您的订单等待
事物。
使用的最简单(也是最有效的方法)等待
在减少的是:
1.使用地图
返回阵列承诺
2。等待
诺言阵列
3。减少
解决值
const reduceloop = async _ => {
console.log(“ start”);const Prolise = fruitstoget.map(getnumfruit);
const numfruits =等待Promise.All(承诺);
const sum = numfruits.Reduce((sum,fruit)=> sum + crual);console.log(sum);
console.log(“ end”);
};
此版本易于阅读和理解,并且需要一秒钟来计算水果总数。
关键要点
1.如果要执行等待
串联呼叫,使用循环
(或任何没有回调的循环)。
2.永远不要使用等待
和foreach
。用一个循环
(或任何没有回调的循环)。
3.不要等待
里面筛选
和减少
。总是等待
一系列承诺地图
, 然后筛选
要么减少
因此。