背景
这是一次需要**“遍历数组按顺序执行异步方法”**而引发的血案,看起来比较简单,但是这个看起来很小的问题却有很多种值总结的地方,我来慢慢分析。
异步方法有哪几种
- 回调函数
//1s之后执行callback函数
function a(callback){
setTimeout(()=>{
callback()
},1000)
}
- 事件监听
document.getElementById("myBtn").addEventListener("click", function(){
document.getElementById("demo").innerHTML = "Hello World";
});
- Promise对象 返回的是一个Promise对象,这个对象可以使用.then,.catch方法回调函数
new Promise((resolve)=>{
setTimeout(()=>{
resolve(1)
},1000)
})
.then(()=>{})
.catch(()=>{})
- 发布/订阅
//在RXJS中
import { interval } from 'rxjs';
const observable = interval(1000); //每隔1S输出一个数字(0,1,2,3...)的发布者
const subscription = observable.subscribe(x => console.log(x)); //订阅者 获取到信息并打印
// 取消通过观察者订阅正在开始执行的 Observable
subscription.unsubcribe();
Promise对象
new Promise((resolve) => {
setTimeout(()=>{
resolve(1000)
},1000)
})
这四种方法的根本其实都是利用浏览器定时器的工作原理
按顺序执行异步方法
//使用await async语法糖
for (let i = 0, len = list.length; i < len; i++) {
pendingList.push(await new Promise((resolve) => {
setTimeout(() => {
console.log(list[i])
resolve(list[i])
}, 500)
}))
}
console.log(pendingList)
//打印结果:
for循环
1
2
3
4
[1, 2, 3, 4]
- 第一个脑洞(仅仅这是for循环对于其他的forEach,map,reduce等)
try {
console.log('forEach循环')
pendingList = []
list.forEach(async (item) => {
pendingList.push(await new Promise((resolve) => {
setTimeout(() => {
console.log(item)
resolve(item)
}, 500)
}))
})
console.log(pendingList)
} catch (error) {
console.log(error)
}
//打印结果:
forEach循环
[]
上述代码中使用forEach并没有得到想要的结果,这就是另外一个注意点,循环方法中,因为map,forEach,filters这些循环方法是直接调用回调函数,await并没有起作用,for/for..of是通过迭代器方式去遍历,await有效
- 第二个脑洞 看题:按顺序执行异步,使用下面函数查看更直观 使用定时器,无法确认顺序,因为相同的时间,先执行的肯定先有callback
pendingList = list.reduce((current, item) => {
return [
...current,
new Promise((resolve) => {
setTimeout(() => {
console.log(item)
resolve(item)
}, 500)
})
]
}, [])
res = await Promise.all(pendingList)
console.log(res)
//打印的结果:
1
2
4
[1, 2, 3, 4]
// 1秒之内只执行一次方法 更加直观
onceAction (value, callback) {
clearTimeout(this.time)
this.time = null
sessionStorage.setItem('value', value)
this.time = setTimeout(() => {
console.log(sessionStorage.getItem('value'))
callback()
}, 1000)
},
- 第三个脑洞(除了await,async使用其他的方法按顺序执行)
//简单的递归数组
//this.cucleFN([1, 2, 3, 4])
cycleFn (list) {
if (list.length === 0) {
return false
}
let dealList = [...list]
const value = (dealList.splice(0, 1))[0]
this.onceAction(value, () => {
this.cucleFN(dealList)
})
}