化繁为简,用几个例子介绍JavaScript异步处理async awite
假如现在有三批任务需要处理,任务数分别为 [9,15,9],任务每次处理需要1秒中
async 关键字
首先,我们使用 async 关键字,把它放在函数声明之前,使其成为 async function。异步函数是一个知道怎样使用 await 关键字调用异步代码的函数。
将 async 关键字加到函数申明中,可以告诉它们返回的是 promise,而不是直接返回值。此外,它避免了同步函数为支持使用 await 带来的任何潜在开销。在函数声明为 async 时,JavaScript引擎会添加必要的处理,以优化你的程序。爽!
每次执行任务函数,函数为异步函数,模拟API异步请求, 耗时大约1秒钟:
我们已经将代码封装在函数中,并且我们在 function 关键字之前包含了 async 关键字。这是必要的 –– 您必须创建一个异步函数来定义一个代码块,在其中运行异步代码; await 只能在异步函数内部工作。
// 等待时间
function sleepAsync(delay) {
return new Promise((resolve) => {
setTimeout(resolve, delay)
})
}
async function TaskBody(message) {
// 等待随机*秒钟
time = 750 + Math.floor(Math.random() * 500);
await sleepAsync(time);
var ts = new Date().format("yyyy-MM-dd hh:mm:ss.S");
console.log(ts + '| 完成:' + message + ' 耗时:' + time)
}
1、同步执行任务
await
异步函数改成同步,函数前需要添加 async
关键字
await关键字:
当 await 关键字与异步函数一起使用时,它的真正优势就变得明显了 —— 事实上, await 只在异步函数里面才起作用。它可以放在任何异步的,基于 promise 的函数之前。它会暂停代码在该行上,直到 promise 完成,然后返回结果值。在暂停的同时,其他正在等待执行的代码就有机会执行了。
您可以在调用任何返回Promise的函数时使用 await,包括Web API函数。
在asyncTest()函数定义中,您可以看到代码与先前的 promise 版本非常相似,但存在一些差异。不需要附加 .then() 代码块到每个promise-based方法的结尾,你只需要在方法调用前添加 await 关键字,然后把结果赋给变量。await 关键字使JavaScript运行时暂停于此行,允许其他代码在此期间执行,直到异步函数调用返回其结果。一旦完成,您的代码将继续从下一行开始执行。例如:
async function asyncTest() {
console.log('================测试开始')
// 任务批次 分三批,没批分别执行任务次数 3,15,9
var dataSource = [3, 15, 9]
for (var index = 0; index < dataSource.length; index++) {
// 本批次需要循环执行的任务次数
count = dataSource[index]
for (var i = 0; i < count; i++) {
// 执行任务
await TaskBody(`任务ID:${index + 1} - ${i + 1}`);
}
}
console.log('================测试结束')
}
这个操作共需要耗时 (3+15+9)*1 = 27 秒
执行结果如下:
================测试开始
2022-01-21 21:50:50.939| 完成:任务ID:1 - 1 耗时:1056
2022-01-21 21:50:51.820| 完成:任务ID:1 - 2 耗时:878
2022-01-21 21:50:52.872| 完成:任务ID:1 - 3 耗时:1038
2022-01-21 21:50:53.728| 完成:任务ID:2 - 1 耗时:852
2022-01-21 21:50:54.942| 完成:任务ID:2 - 2 耗时:1213
2022-01-21 21:50:55.847| 完成:任务ID:2 - 3 耗时:888
2022-01-21 21:50:57.515| 完成:任务ID:2 - 4 耗时:1045
2022-01-21 21:50:58.319| 完成:任务ID:2 - 5 耗时:777
2022-01-21 21:50:59.565| 完成:任务ID:2 - 6 耗时:1236
2022-01-21 21:51:00.752| 完成:任务ID:2 - 7 耗时:1172
2022-01-21 21:51:01.896| 完成:任务ID:2 - 8 耗时:1117
2022-01-21 21:51:03.41| 完成:任务ID:2 - 9 耗时:1139
2022-01-21 21:51:04.217| 完成:任务ID:2 - 10 耗时:1161
2022-01-21 21:51:05.161| 完成:任务ID:2 - 11 耗时:932
2022-01-21 21:51:06.425| 完成:任务ID:2 - 12 耗时:1249
2022-01-21 21:51:07.462| 完成:任务ID:2 - 13 耗时:1030
2022-01-21 21:51:08.514| 完成:任务ID:2 - 14 耗时:1041
2022-01-21 21:51:09.532| 完成:任务ID:2 - 15 耗时:1010
2022-01-21 21:51:10.529| 完成:任务ID:3 - 1 耗时:988
2022-01-21 21:51:11.299| 完成:任务ID:3 - 2 耗时:761
2022-01-21 21:51:12.298| 完成:任务ID:3 - 3 耗时:990
2022-01-21 21:51:13.365| 完成:任务ID:3 - 4 耗时:1054
2022-01-21 21:51:14.548| 完成:任务ID:3 - 5 耗时:1176
2022-01-21 21:51:15.585| 完成:任务ID:3 - 6 耗时:1035
2022-01-21 21:51:16.620| 完成:任务ID:3 - 7 耗时:1020
2022-01-21 21:51:17.572| 完成:任务ID:3 - 8 耗时:942
2022-01-21 21:51:18.395| 完成:任务ID:3 - 9 耗时:810
================测试结束
2、异步执行
第一步结果我们可以看到这是个耗时的操作,现在我们改造一下,异步调用
function asyncTest() {
console.log('================测试开始')
// 任务批次 分三批,没批分别执行任务次数 3,15,9
var dataSource = [3, 15, 9]
for (var index = 0; index < dataSource.length; index++) {
// 本批次需要循环执行的任务次数
count = dataSource[index]
for (var i = 0; i < count; i++) {
// 执行任务
TaskBody(`任务ID:${index + 1} - ${i + 1}`);
}
}
console.log('================测试结束')
}
执行结果:
================测试开始
================测试结束
2022-01-21 21:58:41.467| 完成:任务ID:3 - 3 耗时:1116
2022-01-21 21:58:41.467| 完成:任务ID:2 - 15 耗时:1116
2022-01-21 21:58:41.514| 完成:任务ID:2 - 9 耗时:1116
2022-01-21 21:58:41.545| 完成:任务ID:2 - 4 耗时:1116
2022-01-21 21:58:41.546| 完成:任务ID:3 - 6 耗时:1116
2022-01-21 21:58:41.608| 完成:任务ID:2 - 10 耗时:1116
2022-01-21 21:58:41.655| 完成:任务ID:2 - 3 耗时:1116
2022-01-21 21:58:41.655| 完成:任务ID:2 - 11 耗时:1116
2022-01-21 21:58:41.655| 完成:任务ID:3 - 2 耗时:1116
2022-01-21 21:58:41.670| 完成:任务ID:3 - 7 耗时:1116
2022-01-21 21:58:41.671| 完成:任务ID:2 - 6 耗时:1116
2022-01-21 21:58:41.686| 完成:任务ID:3 - 4 耗时:1116
2022-01-21 21:58:41.702| 完成:任务ID:3 - 8 耗时:1116
2022-01-21 21:58:41.732| 完成:任务ID:1 - 2 耗时:1116
2022-01-21 21:58:41.747| 完成:任务ID:2 - 7 耗时:1116
2022-01-21 21:58:41.747| 完成:任务ID:2 - 1 耗时:1116
2022-01-21 21:58:41.747| 完成:任务ID:2 - 5 耗时:1116
2022-01-21 21:58:41.763| 完成:任务ID:2 - 12 耗时:1116
2022-01-21 21:58:41.763| 完成:任务ID:1 - 1 耗时:1116
2022-01-21 21:58:41.794| 完成:任务ID:2 - 13 耗时:1116
2022-01-21 21:58:41.841| 完成:任务ID:3 - 9 耗时:1116
2022-01-21 21:58:41.841| 完成:任务ID:2 - 8 耗时:1116
2022-01-21 21:58:41.841| 完成:任务ID:3 - 5 耗时:1116
2022-01-21 21:58:41.888| 完成:任务ID:2 - 2 耗时:1116
2022-01-21 21:58:41.903| 完成:任务ID:1 - 3 耗时:1116
2022-01-21 21:58:41.918| 完成:任务ID:3 - 1 耗时:1116
2022-01-21 21:58:41.950| 完成:任务ID:2 - 14 耗时:1116
3、等待所有异步任务完成
现在有新需求,要等待所有异步完成后,才输出 测试结束
Promise.all(iterable)
这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致;如果这个新的promise对象触发了失败状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息。Promise.all方法常被用于处理多个promise对象的状态集合。参考地址
function asyncTest() {
console.log('================测试开始')
// 任务批次 分三批,没批分别执行任务次数 3,15,9
var dataSource = [3, 15, 9]
var tks = []
for (var index = 0; index < dataSource.length; index++) {
// 本批次需要循环执行的任务次数
count = dataSource[index]
for (var i = 0; i < count; i++) {
// 执行任务
var tk = TaskBody(`任务ID:${index + 1} - ${i + 1}`);
tks.push(tk)
}
}
// 等待所有任务都执行完毕
Promise.all(tks).then(() => {
console.log('================测试结束')
});
}
运行结果:
================测试开始
2022-01-21 22:03:05.976| 完成:任务ID:3 - 7 耗时:1117
2022-01-21 22:03:05.976| 完成:任务ID:2 - 8 耗时:1117
2022-01-21 22:03:05.991| 完成:任务ID:2 - 4 耗时:1117
2022-01-21 22:03:06.53| 完成:任务ID:2 - 3 耗时:1117
2022-01-21 22:03:06.69| 完成:任务ID:3 - 4 耗时:1117
2022-01-21 22:03:06.70| 完成:任务ID:2 - 14 耗时:1117
2022-01-21 22:03:06.85| 完成:任务ID:2 - 12 耗时:1117
2022-01-21 22:03:06.148| 完成:任务ID:3 - 2 耗时:1117
2022-01-21 22:03:06.195| 完成:任务ID:2 - 5 耗时:1117
2022-01-21 22:03:06.226| 完成:任务ID:2 - 6 耗时:1117
2022-01-21 22:03:06.227| 完成:任务ID:1 - 2 耗时:1117
2022-01-21 22:03:06.273| 完成:任务ID:1 - 1 耗时:1117
2022-01-21 22:03:06.274| 完成:任务ID:2 - 2 耗时:1117
2022-01-21 22:03:06.289| 完成:任务ID:3 - 5 耗时:1117
2022-01-21 22:03:06.320| 完成:任务ID:2 - 9 耗时:1117
2022-01-21 22:03:06.321| 完成:任务ID:3 - 9 耗时:1117
2022-01-21 22:03:06.335| 完成:任务ID:3 - 3 耗时:1117
2022-01-21 22:03:06.351| 完成:任务ID:2 - 15 耗时:1117
2022-01-21 22:03:06.352| 完成:任务ID:2 - 11 耗时:1117
2022-01-21 22:03:06.366| 完成:任务ID:2 - 13 耗时:1117
2022-01-21 22:03:06.367| 完成:任务ID:2 - 10 耗时:1117
2022-01-21 22:03:06.381| 完成:任务ID:2 - 7 耗时:1117
2022-01-21 22:03:06.396| 完成:任务ID:2 - 1 耗时:1117
2022-01-21 22:03:06.396| 完成:任务ID:3 - 1 耗时:1117
2022-01-21 22:03:06.412| 完成:任务ID:3 - 8 耗时:1117
2022-01-21 22:03:06.428| 完成:任务ID:3 - 6 耗时:1117
2022-01-21 22:03:06.443| 完成:任务ID:1 - 3 耗时:1117
================测试结束
4、按批次执行异异步任务
有时候,我们执行下一批任务的时候,可能会用到第一批任务的结果,所以需要在执行第二批任务的时候确保第一批任务都已执行完毕
async function asyncTest() {
console.log('================测试开始')
// 任务批次 分三批,没批分别执行任务次数 3,15,9
var dataSource = [3, 15, 9]
for (var index = 0; index < dataSource.length; index++) {
// 本批次需要循环执行的任务次数
count = dataSource[index]
var tks = []
for (var i = 0; i < count; i++) {
// 执行任务
var tk = TaskBody(`任务ID:${index + 1} - ${i + 1}`);
tks.push(tk)
}
// 等待本批次所有任务都执行完毕
await Promise.all(tks)
}
console.log('================测试结束')
}
执行结果:
================测试开始
2022-01-21 22:06:35.269| 完成:任务ID:1 - 3 耗时:903
2022-01-21 22:06:35.316| 完成:任务ID:1 - 2 耗时:903
2022-01-21 22:06:35.317| 完成:任务ID:1 - 1 耗时:903
2022-01-21 22:06:36.192| 完成:任务ID:2 - 15 耗时:860
2022-01-21 22:06:36.250| 完成:任务ID:2 - 5 耗时:860
2022-01-21 22:06:36.266| 完成:任务ID:2 - 8 耗时:860
2022-01-21 22:06:36.329| 完成:任务ID:2 - 1 耗时:860
2022-01-21 22:06:36.345| 完成:任务ID:2 - 4 耗时:860
2022-01-21 22:06:36.392| 完成:任务ID:2 - 10 耗时:860
2022-01-21 22:06:36.454| 完成:任务ID:2 - 7 耗时:860
2022-01-21 22:06:36.454| 完成:任务ID:2 - 13 耗时:860
2022-01-21 22:06:36.500| 完成:任务ID:2 - 2 耗时:860
2022-01-21 22:06:36.516| 完成:任务ID:2 - 3 耗时:860
2022-01-21 22:06:36.531| 完成:任务ID:2 - 11 耗时:860
2022-01-21 22:06:36.531| 完成:任务ID:2 - 12 耗时:860
2022-01-21 22:06:36.531| 完成:任务ID:2 - 6 耗时:860
2022-01-21 22:06:36.561| 完成:任务ID:2 - 14 耗时:860
2022-01-21 22:06:36.577| 完成:任务ID:2 - 9 耗时:860
2022-01-21 22:06:37.382| 完成:任务ID:3 - 8 耗时:1038
2022-01-21 22:06:37.470| 完成:任务ID:3 - 7 耗时:1038
2022-01-21 22:06:37.495| 完成:任务ID:3 - 5 耗时:1038
2022-01-21 22:06:37.496| 完成:任务ID:3 - 3 耗时:1038
2022-01-21 22:06:37.587| 完成:任务ID:3 - 4 耗时:1038
2022-01-21 22:06:37.618| 完成:任务ID:3 - 9 耗时:1038
2022-01-21 22:06:37.664| 完成:任务ID:3 - 6 耗时:1038
2022-01-21 22:06:37.664| 完成:任务ID:3 - 1 耗时:1038
2022-01-21 22:06:37.758| 完成:任务ID:3 - 2 耗时:1038
================测试结束