前言
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是并行的迭代,而不是同步的循环,切记!切记!切记!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。