Promise在循环体中需要注意的一些事项
2018-03-27 本文已影响111人
黑火巨雷
我们首先知道,promise的then返回的始终是一个新promise。那么就存在下述情况
let aPromise = new Promise((resolve,reject)=>{
resolve('aPromise')
})
aPromise.then(res=>'bPromise')
aPromise.then(res=>{console.log(res)}) // 实际上打印的是'aPromise'
then不是改变aPromise的内容,而是始终返回新promise。
如此,当我们要写一个循环体的时候。就需要一个游标来记录每次执行完的then。
let delayPrint = (i = 1000) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(i);
resolve('ok:'+i)
}, 1000)
})
}
let forbody = Promise.resolve('begin');
for (let i = 1; i <= 5; i++) {
forbody = forbody.then(res=>{
return delayPrint(i);
})
}
我们的forbody每次重新记录forbody.then返回的新promise。这样下次执行forbody.then的时候总能记录正确的位置。利用这一特性,我们可以制造循环体。上述代码可以依次打印1
,2
,3
,4
,5
。需要注意的是如果把console.log(i)放入then中,变成console.log(res)会存在一个业务位置的问题。区别在于打印完5
后立即结束,还是会再执行下一个而不打印。原因时i=5时执行完成后还会return一个delayPrint,相当于多执行了一次。虽然没有下次的then。
也有一些比较丑陋的解决方案。如
let forbody = Promise.resolve('begin');
for (let i = 1; i <= 5; i++) {
forbody = forbody.then(res=>{
i!=5 && return delayPrint(i*1000);
})
}
改良方案,用其他方式实现循环体
面对定长的的循环体。可以用遍历数组 + Promise.all的方式来实现。
使用这种方式很直观,并且在执行完成后能一次性得到所有的resolve