手写async await的以及深度挖掘(一挖到底)
1. 基本知识(基础)
1. promise的原理及其链式调用
const promise = new Promise((res,rej)=>{
res(1) // res 和rej 执行里面的参数,将会作为参数放在.then((res)=>{})的res去接收
rej(2) // promise特性,状态值一旦改变就不能再继续改,所以此操作无效
});
promise.then(res=>{
console.log(res); // res =>1
// 如果一个promise返回一个常量,那么这个常量将会作为实参放到。then的回调res参数上
// 如果是promise,将会执行这个promise,将结果放到res上
return new Promise((res,rej)=>res(2))
}).then(res=>{
console.log(res); // res =>2
})
2. async/await基本用法
async function name() {
console.log('async', 'await之前');
const b = await promise()
console.log('async', 'await之后', b);
console.log('async', 'await之前');
const b1 = await promise()
console.log('async', 'await之后', b1);
}
name() // 异步请求将会如同同步一样执行,进行输出
3. generator基本用法
// generator 特性,每一次调用他的next方法,他会停留在下一个yield位置
function* generator() {
console.log('generator', 'yield之前');
const b = yield promise()
console.log('generator', 'yield之后', b);
console.log('generator', 'yield之前');
const b1 = yield promise()
console.log('generator', 'yield之后', b1);
}
// 生成generator迭代器
const gen =generator();
gen.next()// 代码会执行第一个yield这里停住,不会执行,并把yield后面的表达式存起来(value里)
gen.next(3) // 同第一个generator函数,但是此处next(val),val将会作为结果返回给b,并往下执行,执行到下一个yield时停止
2. 简易实现2个await的逻辑
1. generator.next 的返回值
const dataPromise =gen.next()
// {value:Promise,done: false},value为yield后的表达式,done为后面是否还有field
2. 利用promise与generator简易实现
// 利用1-2的代码往下执行
var gen = generator() // 调用生成迭代器
var datapromise = gen.next() // 代码会停留在带一个yield这里
datapromise.value.then(value => {
console.log(value, 'value'); // 这里我们会发现value就是promise返回的值,
const nextYield = gen.next(value)
// next执行一次,代码会执行到下一个yield之前,并将value作为前一个await的返回值
return nextYield.value
// 如果里面存在多个await,利用promise的特性,进行链式调用
//如果promise返回了一个新的promise,那么可以连续。then让他执行)
}).then(value => {
console.log('第二个await', value);
})
3. async/await完整实现
- 利用generator特性,实现阻塞等待进程结束
- 函数内部先生成generator函数。
- 每next执行一次,步数就前进一步,可以实现链式调用
- 获取generator的返回值,value 为yield的后面值或者函数,done 为状态标识是否结束
- 通过循环递归把每个promise函数执行(这里没做是否是promise的校验,只是简易实现)
- 做好异常兜底和异常报错
1.generator函数,后面我们需要用我们封装的去检验这个
function* generator() {
const b = yield new Promise((res,rej)=>{
setTimeout(()=>{res('第一次await')},1000)
})
console.log('我是中间');
const b1 = yield new Promise((res,rej)=>{
setTimeout(()=>{res('第二次await')},2000)
})
}
2.newGenerator通过promise加上递归实现模拟操作
function newGenerator(generatorFunc, ...args) {
const gen = generatorFunc(...args) // 生成一个generator函数;
return new Promise((resolve, reject) => {
// 利用递归不断的去轮训
function step(key, arg) {
let generatorResult;
try {
generatorResult = gen[key](arg);
} catch (error) {
return reject(error);
}
const { value, done} = generatorResult;
// done 代表后续是还有await
if (done) {
resolve(value);// 结束递归,因为已经么有await需要处理了
return;
}
return Promise.resolve(value).then(res =>{
console.log(res,'await结果');
step(key, res)
}).catch(err => {
step('throw', err)
});
}
step('next') // 首次执行
})
}
newGenerator(generator)
4. async/await源码挖掘
1.把代码进行编译
编译地址
async function a(){
var start=1
await new Promise((res)=>{res(1)});
var end=2
}
2.主执行函数
- 函数将会别编译成由一个高阶函数去执行
- awaiter的入参中包含 generator函数
- 可以看出函数里所有await都被收集起来按照顺序去执行
function a() {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: var start=1; return [4 /*yield*/, new Promise(function (res) { res(1); })];
case 1:
_a.sent();
var end=2
return [2 /*return*/];
}
});
});
}
3.__awaiter函数处理promise
- 此时__awaiter首次入参为
this, void 0,void 0,__generator
- 利用递归去处理循环所有await,将所有非promise处理成promise
- 调用genertor,来完成最终的行为
- 大致思路和我们封装的相似的狠
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
// 检验是否是个promise,如不是则生成成个promise,并将value值resolve出去
function adopt(value) {
return value instanceof P ? value : new P(function (resolve) {
resolve(value);
});
}
return new(P || (P = Promise))(function (resolve, reject) {
// 成功态方法
function fulfilled(value) {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
}
// 失败态方法
function rejected(value) {
try {
step(generator["throw"](value));
} catch (e) {
reject(e);
}
}
// 通过递归不断执行,直到完成所有await;并把不是promise的值处理成promise;
function step(result) {
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
}
// 首次启动
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
4.__generator函数阻塞进程派发事件
源码代码看起来晦涩难懂,下面继续模拟实现generator
主要实现思想是先将代码根据yield去分割,然后按顺序去执行,一次前进一步
- 我们我们可以利用class类去做一个构造函数,这样就可以保证每个实例拥有自己的状态
class Context {
constructor() {
this.prev = 0 // 当前位置
this.next = 0 // 前进一步
this.done = false // 是否结束状态
}
stop() {
this.done = true
}
}
- 我们观察原gnertor的的打印,实例初始化之后,发现每次next时都会retrun {value,done},于是我们
function foo() {
var context = new Context // 生成实例,并利用闭包缓存起来
return {
next: function () {
var value = gen$(context); // gen$ 为处理步数前进和结束的函数
var done = context.done
return {
value,
done
}
}
}
}
- 迭代器会将yield后的代码进行切割并按顺序存储到代码里,之前的代码会放到before里,之后的代码将会放到after里
function gen$(context) {
console.log(context);
switch (context.prev = context.next) {
case 0:
let before;
context.next = 1;
return 'result1';
case 1:
context.next = 2;
return 'result2';
case 3:
context.stop();
let after;
return 'ending';
}
}
- 经过转译过得源码
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
function a() {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, a = 1];
case 1:
_a.sent();
return [2 /*return*/];
}
});
}