手写async await的以及深度挖掘(一挖到底)

手写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*/];
        }
    });
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

家雀安知鸿鹄志

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值