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”);
};
控制台显示“开始”。一秒钟后,它登录27.另一秒钟后,它登录0。另一秒钟后,它记录了14和“ 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; index const fruit = fruitstoget [index];
const numfruit =等待getnumfruit(水果);
console.log(numfruit);
}
console.log(“ end”);
};

当您使用时等待,您希望JavaScript暂停执行,直到已期待的承诺得到解决为止。这表示等待循环中的s应串联执行。

结果就是您所期望的。

“开始”;
“苹果:27”;
“葡萄:0”;
“梨:14”;
“结尾”;
控制台显示“开始”。一秒钟后,它登录27.另一秒钟后,它登录0。另一秒钟后,它记录了14和“ end”

这种行为与大多数循环一起起作用(例如尽管对于循环)...

但是它不适用于需要回调的循环。需要后备的循环的示例包括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’
控制台记录“开始”和“结束”。一秒钟后,它记录了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]”;
“结尾”;
控制台日志“开始”。一秒钟后,它记录了‘[27、0、14]和“ end”

如果您愿意,您可以操纵您在承诺中返回的价值。解决的值将是您返回的值。

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”);
};
启动[“苹果”];
结尾;
控制台显示“开始”。一秒钟后,控制台记录“ ['苹果”]和“ 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”;
“结尾”;
主机日志“开始”,“ 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”和“ end”

什么?![对象承诺] 14?!

解剖这很有趣。

  • 在第一次迭代中,0Numfruit是27(从getnumfruit(“苹果”))。0 + 27是27。
  • 在第二次迭代中,是诺言。(为什么?因为异步函数总是返回承诺!)Numfruit是0。不能正常将承诺添加到一个对象,因此JavaScript将其转换为[对象承诺]细绳。[对象承诺] + 0[对象承诺] 0
  • 在第三次迭代中,也是一个诺言。Numfruit14[对象承诺] + 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”;
“结尾”;
控制台日志“开始”。三秒钟后,它记录了“ 41”和“ end”

但是...从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”);
};
控制台日志“开始”。一秒钟后,它记录了“ 41”和“ 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”);
};

此版本易于阅读和理解,并且需要一秒钟来计算水果总数。

控制台日志“开始”。一秒钟后,它记录了“ 41”和“ end”

关键要点

1.如果要执行等待串联呼叫,使用循环(或任何没有回调的循环)。

2.永远不要使用等待foreach。用一个循环(或任何没有回调的循环)。

3.不要等待里面筛选减少。总是等待一系列承诺地图, 然后筛选要么减少因此。

本文最初发布我的博客
注册我的通讯如果您希望更多文章帮助您成为更好的前端开发人员。

- -

- -

获取中型应用betway娱乐官网

一个说“在应用商店上下载”的按钮,如果单击,它将带您到iOS App Store
一个说“获取它,Google Play”的按钮,如果单击它,它将带您到Google Play商店
Baidu