遍历数组按顺序执行异步引发的思考

2,943 阅读2分钟

背景

这是一次需要**“遍历数组按顺序执行异步方法”**而引发的血案,看起来比较简单,但是这个看起来很小的问题却有很多种值总结的地方,我来慢慢分析。

异步方法有哪几种

  1. 回调函数
//1s之后执行callback函数
function a(callback){
  setTimeout(()=>{
    callback()
  },1000)
}
  1. 事件监听
document.getElementById("myBtn").addEventListener("click", function(){  
   document.getElementById("demo").innerHTML = "Hello World";
});
  1. Promise对象 返回的是一个Promise对象,这个对象可以使用.then,.catch方法回调函数
new Promise((resolve)=>{
	setTimeout(()=>{
    	resolve(1)
    },1000)
})
.then(()=>{})
.catch(()=>{})
  1. 发布/订阅
//在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)
 })
}