3

前言

Js中有令人头疼的地狱回调的问题,于是有了Promise,有了链式调用,基于Promise也出现了各种异步解决方案就出现了,其中最好的个人感觉应该是async/await的解决方案了,一直以来都是模模糊糊的使用,直到遇见了循环与异步的问题,于是花时间查阅资料从1分懂到了4分懂.

关于Promise

网上关于Promise的文章有很多,也讲得很好,这里也简单的记录一下.

基本概念

Promise不仅通过链式调用解决了回调嵌套的问题,还解决了回调函数中使用 return 和 throw 的问题,最重要的是它在异步解决方案中的使用.
Promise一共拥有pending/resloved/rejected(进行中/已完成/已失败)这三个状态,resloved/rejected状态可以由pendding状态变化得到,而且这个状态一旦变化就不会再更改.

常用API

  • Promise.all() // 多个Promise并行进行,当所有Promise reslove时reslove.当有一个Promise reject时reject
  • Promise.race() // 与Promise.all类似,差别在当第一个Promise reslove时reslove.
  • Promise.then() // 用于链式调用
  • Promise.catch() // 捕获reject

关于异步的栗子

function sleep(dely) {
  return new Promise((reslove) => {
    setTimeout(() => {
      console.log(`dely ${dely}s`)
      reslove();
    }, dely * 1000);
  })
}

(async () => {
  console.log('before sleep')
  await sleep(2);
  console.log('after sleep')
})()

/** console
* before sleep
* dely 2s
* after sleep
*/

关于 async/await

这个是ES7中的异步解决方案,在Node v8.0.0及其以上已经原生支持,在低版本的Node中需要开启--harmony-async-await 选项

如何使用

  • async 表示这是一个async函数,await只能用在这个函数里面。
  • await 表示在这里等待Promise返回结果了,再继续执行。
  • await 后面跟着的应该是一个Promise对象
    栗子可以参考上一个栗子,使用async/await不仅语义明确,操作简单,而且还可以同步捕获错误哦.

循环与异步操作

例如有如下设定: 每个人的碎觉时间都是固定值,如果有多个人碎觉,则后一个人的碎觉时间是前面碎觉时间的累积和,写一个函数在传入name数组时能实现以上描述,话不多说上代码

const users = { // 定义3个人,且每个人睡觉时间是1
  leo: {
    name: 'leo',
    sleep: 1
  },
  mick: {
    name: 'mick',
    sleep: 1
  },
  jack: {
    name: 'jack',
    sleep: 1
  }
}

function sleep(name, sleep) { // 碎觉函数,包括打印当前碎觉时间,和累加未碎觉人的时间
  return new Promise((reslove) => {
    setTimeout(() => {
      console.log(`${name} sleep ${sleep}`);
      Object.keys(users).forEach(u => {
        if (name !== u) {
          users[u].sleep += sleep;
        }
      })
      reslove();
    }, sleep * 1000);
  })
}

我第一次写出来是这样的

(function consoleSleep(names) {
  return Promise.all(names.map(async n => {
    await sleep(users[n].name, users[n].sleep)
  }))
}(['leo', 'mick', 'jack']))
/** console
* leo sleep 1
* mick sleep 1
* jack sleep 1
*/

当场我就很懵逼,把name 数组拿来循环,然后循环中等待当前sleep时的各种异步操作,但结果让人很忧桑,在分析问题后,发现这是一个串行执行异步操作的问题,
好吧,Promise.all()是并行进行的,可是在去掉Promise.all后结果任然没有改变,经过各种查资料后发现是map的问题(笑哭)(map和forEach都是并行迭代的),好吧再改代码

(async function consoleSleep(names) {
  for(let i = 0; i < names.length; i++) {
    await sleep(users[names[i]].name, users[names[i]].sleep)
  }
}(['leo', 'mick', 'jack']))

/** console
* leo sleep 1
* mick sleep 2
* jack sleep 4
*/

终于实现了功能,大功告成

小结

Promise不仅解决了回调嵌套还提供了异步解决的方式,可以使用Promise封装异步操作然后使用async/await来把异步操作变成同步的,在循环中使用Promise时要注意并行和串行两种方式带来的不同操作结果,当然其它库也提供了相应封装的方法(例如bluebird有mapSeries可以直接迭代串行执行多个Promise).
ps: 学习了,map/forEach是并行的迭代,而不是同步的循环,切记!切记!切记!


Leo_
669 声望22 粉丝

learning...


引用和评论

0 条评论