async,await的使用和原理
1.使用
async function fn(){
let p1 = await new Promise( (res,rej) =>{
res(1111)
})
console.log(p1)
let p2 = await new Promise( (res,rej) =>{
res(2222) // rej("错误")可以被返回的promise的catch捕获错误信息
})
console.log(p2)
// let bb = await 222 // 被包装成 await Promise Promise.resolve(222)
// console.log(aa) // 可以被返回的promise的catch捕获语法错误信息
return p1 + p2 // 被包装成promise Promise.resolve(1111)
}
fn().then(data =>{
console.log("data",data) // return 返回值 // 不是promise会被包装成promise
}).catch(err =>{
console.log("err",err)
})
2.特点
1.返回promise实例,可以捕获语法错误,捕获await后的失败的状态和结果
2.await 和 return 关键字后面的值 不是promise会被包装成promise Promise.resolve(value)
3.原理
问题 async中 await 等待效果是如何实现的 ,即看起来就是异步任务阻塞了后续同步代码的执行 ?
答 async 函数在编译后将代码以await进行了分割和分片 , 递归调用next方法,通过_context.next 来执行分割好的代码片段,下一次要执行的代码片段是在上一次的promise.then中执行的,所以代码执行必须promise状态改变后才行,所以会有异步阻塞的效果
以下是具体分析
- async await是 generator 和 co 的语法糖
- generator 返回一个迭代器对象含有next方法,next方法传递实参赋值给上一次yeild的返回值
- co 就是自动递归调用迭代器的next方法,并拿到value值,并在promise的then中递归调用
- async 实现了自己的co递归调用
5. 最为关键的一点是 async 和 yeild在编译后将代码进行了分割(猜测根据ast语法树)
分割点为await和yeild关键字,从而实现了代码的分片,分段执行
6.通过_context.next指针来决定下一次执行哪一段代码
7.以上流程可由下图简单表示
a. generator 和 co (实现)的使用如下
function* read(){
let a = yield 'node'
console.log(a) // 222
let b = yield new Promise( (res,rej)=>{
res("vue")
})
console.log(b) // 333
return a + b
}
let it = read() // 返回迭代器对象
console.log(it.next(111)) // {value: "node", done: false}
console.log(it.next(222)) // {value: Promise实例, done: false}
console.log(it.next(333)) // {value: 555, done: true}
// value 对应 yield关键字后面的值
// next方法传递的实参会赋值给上一次yeild的返回值
// 实现简易co co就是自动递归调用迭代器的next方法,并拿到value值,并在promise的then中递归调用
function co(it){
return new Promise( (res,rej) =>{
function next(data){
let {value,done} = it.next(data)
if(!done){
// value如果不是promise将其包装成promise Promise.resolve(value)
Promise.resolve(value).then(data =>{
next(data)
},rej)
}else{
return res(value)
}
}
next()
})
}
co(read()).then(data =>{
console.log(data)
})
- 将 async 函数放在babel中可得到编译后的代码,如下
// 自己实现regeneratorRuntime
let regeneratorRuntime = {
mark(outerFn){
return outerFn
},
wrap(innerFn,outerFn){
const _context = {
next : 0,
done :false,
abrupt(type,value){
this.done = true
return value
},
stop(){
this.done = true
}
}
return {
next(data){
_context.sent = data
let value = innerFn(_context)
return {
value,
done :_context.done
}
}
}
}
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try { var info = gen[key](arg); var value = info.value; } // gen.next()
catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
}
else {
Promise.resolve(value).then(_next, _throw); // 递归调用_next,并传递了参数
}
}
function _asyncToGenerator(fn) {
return function () {
var self = this, args = arguments;
return new Promise(function (resolve, reject) {
var gen = fn.apply(self, args); // 迭代器对象 {next:(){..}} //
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
function fn() {
return _fn.apply(this, arguments);
}
function _fn() {
_fn = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
var p1, p2;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return new Promise(function (res, rej) {
res(1111);
});
case 2:
p1 = _context.sent;
console.log(p1);
_context.next = 6;
return new Promise(function (res, rej) {
res(2222); // rej("错误")可以被返回的promise的catch捕获错误信息
});
case 6:
p2 = _context.sent;
console.log(p2); // let bb = await 222 // 被包装成 await Promise Promise.resolve(222)
// console.log(aa) // 可以被返回的promise的catch捕获语法错误信息
return _context.abrupt("return", p1 + p2);
case 9:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return _fn.apply(this, arguments);
}
fn().then(data =>{
console.log(data) // 333
})