共计 3661 个字符,预计需要花费 10 分钟才能阅读完成。
1. 其余异步操作
- 传统的异步操作
- Generator 的异步操作
异步:工作的不间断执行,跳跃执行,在一个工作阻塞时主线程去执行其余工作,阻塞工作接管到执行信号时,再从新增加到工作队列中执行
1.1 传统的异步操作
传统的异步操作
- 回调函数
- 事件监听
- 公布 / 订阅
- Promise 对象
1.1.1 回调函数(callback)
-
将一个工作分成多段,嵌套的模式去执行
fs.readFile('/etc/passwd' , 'utf-8', function(err , data){if (err) throw err; console.log(data); });
readFile 的第三个参数就是一个回调函数,在申请文件胜利后才会调用
回调函数能够实现程序的异步执行
回调天堂 callback hell
// 不同的工作
function toEat(val, fn) {setTimeout(() => {fn(val)
}, 2000);
}
function toDrink(val, fn) {setTimeout(() => {fn(val)
}, 1000);
}
function toKTV(val, fn) {setTimeout(() => {fn(val)
}, 500);
}
// 按程序执行工作
toDrink("吃火锅", data => {console.log(data);
toDrink("喝啤酒", data => {console.log(data);
toKTV("唱歌", data => {console.log(data);
})
})
})
//"吃火锅"
//"喝啤酒"
//"唱歌"
在一个工作的回调函数中嵌套其余工作
其余工作也有回调函数,这样的层层嵌套当呈现谬误时代码很难保护
1.1.2 Promise
// 工作封装 Promise
function toResolve(data, delay) {const promise = new Promise((resolve, reject) => {setTimeout(() => {resolve(data)
}, delay);
})
return promise
}
// 不同的工作
function toEat(val) {return toResolve(val, 2000)
}
function toDrink(val) {return toResolve(val, 1000)
}
function toKTV(val) {return toResolve(val, 500)
}
// 按程序执行工作
toEat("吃火锅").then(val => {console.log(val);
return toDrink("喝啤酒")
}).then(val => {console.log(val);
return toKTV("唱歌")
}).then(val => {console.log(val);
}).catch(res => {console.log(res);
})
// 吃火锅
// 喝啤酒
// 唱歌
定时执行回调函数改为定时扭转 Promise 状态
构造更加清晰
然而因为原工作的 Promise 封装,代码冗余,因为 then 的沉积,语义变得不分明
1.2.Generator 的异步操作
依然应用下面封装的 Promise 工作,应用 Generator 去异步执行
// 生成器函数
function* asyncJob() {let val1 = yield toEat("吃火锅")
console.log(val1);
let val2 = yield toDrink("喝啤酒")
console.log(val2);
let val3 = yield toKTV("唱歌")
console.log(val3);
}
// 生成器函数的执行器
function runGenerator(fn) {let iter = fn()
let {value, done} = iter.next()
value.then(val1 => {let { value, done} = iter.next(val1)
value.then(val2 => {let { value, done} = iter.next(val2)
value.then(val3 => {iter.next(val3)
})
})
})
}
// 开始执行异步工作
runGenerator(asyncJob)
// 吃火锅
// 喝啤酒
// 唱歌
以 toEat 为例,察看异步工作如何执行的
- 调用生成器函数的执行器
- 调用生成器函数,返回迭代器对象
- 第一次调用迭代器上的 next 办法:执行至第一个 yild,调用 toEat 函数,返回一个 Promise 对象作为 next 返回值的 value 属性,解构取 value 的值
- 在 value 上调用 then:接管 resolve 中的值(会提早 2s),通过第二次调用 next 将接管的值赋值给第一个 yield
- 能够在第一个 yield 和第二个 yield 之间解决 toEat 返回的值
- 对于第二个 next 的返回值能够解构进行同样的操作
重点
调动 next 后产生了什么
next 参数传递的准则
Promise 的解决与状态变动
2.async 函数
Generator 的语法糖 – 使 异步
操作更加简略
应用下面封装的 Promise 工作
// 生成器的异步工作
async function asyncJob() {let val1 = await toEat("吃火锅")
console.log(val1);
let val2 = await toDrink("喝啤酒")
console.log(val2);
let val3 = await toKTV("唱歌")
console.log(val3);
}
// 开始执行异步工作
asyncJob()
// 吃火锅
// 喝啤酒
// 唱歌
用
async
代替了生成器中星号 *
,用await
代替生成器中的yield
与生成器的异步操作相比,async 函数不必手写执行器,自带执行器
间接调用
async 函数就能够执行外部的工作
async 函数对 Generator 的改良
- 内置执行器
Generator 函数的执行须要执行器,手写或者引入 co 模块,而 async 函数自带执行器,能够让其像一般函数一样执行 - 更好的语义
async 与 await 比星号 * 与 yield 的语义表白更清晰 - 更广的适用性
co 模块指定 yield 后只能是Thunk 函数
或Promise 对象
,而 async 函数的 await 后能够是Promise 对象
或原始类型的值
-
返回值是 Promise
Genetator 的返回值是迭代器对象
,async 函数返回的是一个Promise 对象
async 函数中有
return
时,默认 return 的值 Promise.resolve()转成 Promise 对象,在内部用 then 能够接管
2.1 async 函数的错误处理
已知 async 返回的是一个 Promise,能够在内部捕捉谬误
// 生成器的异步工作
async function asyncJob() {let val1 = await toEat("吃火锅").then(() => {throw new Error("饭店开张了")
})
console.log(val1);
let val2 = await toDrink("喝啤酒")
console.log(val2);
let val3 = await toKTV("唱歌")
console.log(val3);
}
// 开始执行异步工作
asyncJob().catch(res => {console.log(res);
})
// Error: 饭店开张了
一条 await 后的语句出错时,后续不再执行,间接将谬误抛出
try…catch
// 生成器的异步工作
async function asyncJob() {
try {let val1 = await toEat("吃火锅").then(() => {throw new Error("饭店开张了")
})
console.log(val1);
} catch (e) {console.log(e);
}
try {let val2 = await toDrink("喝啤酒")
console.log(val2);
let val3 = await toKTV("唱歌")
console.log(val3);
} catch (e) {console.log(e);
}
}
// 开始执行异步工作
asyncJob().catch(res => {console.log(res);
})
// Error: 饭店开张了
// 喝啤酒
// 唱歌
相互无影响的工作放在不同的 try…catch 代码块中
相互有影响的工作放入雷同的 try…catch 代码块中
2.2 async 函数中的并发
// 生成器的异步工作
async function asyncJob() {let val = await Promise.all([toEat("吃火锅"), toDrink("喝啤酒")])
console.log(val);
let val3 = await toKTV("唱歌")
console.log(val3);
}
// 开始执行异步工作
asyncJob().catch(res => {console.log(res);
})
// ['吃火锅','喝啤酒']
// "唱歌"
同样也能够应用 Promise.any() Promise.race()等办法