这是我参与8月更文挑战的第30天,活动详情查看:8月更文挑战
五、异步编程实战问题
5.1 Promise 获取数据(串行)之 then 写法注意
我们需要实现一个依次分别延迟 1 秒输出值,一共 5 秒的程序,首先是 Promise 的循环,这个循环就相对来说比较麻烦:
const setDelay = (millisecond) => {
return new Promise((resolve, reject) => {
if (typeof millisecond != 'number') {
reject(new Error('参数必须是number类型'));
}
setTimeout(() => {
resolve(`我延迟了${millisecond}毫秒后输出的`)
}, millisecond)
})
}
我们想做到:“循环串行执行延迟一秒的 Promise 函数”,期望的结果应该是:隔一秒输出我延迟了 1000 毫秒后输出的,一共经过循环 3 次。我们想当然地写出下列的链式写法:
arr = [setDelay(1000), setDelay(1000), setDelay(1000)]
arr[0].then(result => {
console.log(result);
return arr[1];
}).then(result => {
console.log(result);
return arr[2];
}).then(result => {
console.log(result);
})
但是很不幸,你发现输出是并行的!!!也就是说一秒钟一次性输出了3个值!。那么这是什么情况呢?是你把 setDelay(1000) 这个直接添加到数组的时候,其实就已经执行了,注意你的执行语句 (1000)。
那么这样导致的后果是什么呢?也就是说数组里面保存的每个 Promise 状态都是 resolve 完成的状态了,那么你后面链式调用直接 return arr[1] 其实没有去请求,只是立即返回了一个 resolve 的状态。所以你会发现程序是相当于并行的,没有依次顺序调用。
那么解决方案是什么呢?直接函数名存储函数的方式(不执行 Promise)来达到目的:
arr = [setDelay, setDelay, setDelay];
arr[0](1000).then(result=>{
console.log(result);
return arr[1](1000);
}).then(result=>{
console.log(result);
return arr[2](1000);
}).then(result=>{
console.log(result);
})
上述相当于把 Promise 预先存储在一个数组中,在你需要调用的时候,再去执行。当然你也可以用闭包的方式存储起来,需要调用的时候再执行。
5.2 Promise 循环获取数据(串行)之 for 循环
const p = arr.reduce((total, current) => {
return total.then((result) => {
console.log(result);
return current()
})
}, Promise.resolve('程序开始'))
p.then((result) => {
console.log('结束了', result);
})
5.3 async/await 循环获取数据(串行)之 for 循环
(async () => {
arr = [timeout(2000), timeout(1000), timeout(1000)]
for (var i = 0; i < arr.length; i++) {
result = await arr[i]();
console.log(result);
}
})()