JavaScript 系列之异步编程(五)

401 阅读2分钟

这是我参与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);
  }
})()

相关文章