async 异步实现原理

async 异步实现原理

async 异步

async 函数是 ES2017 标准引入的,其与 await 语法搭配,用来更优雅地实现异步编程。与 generator 函数作用类似。不同的是,async 不需要手动调用 next 方法,并且返回值始终是 Promise 对象。

async 函数可以看作多个异步操作,包装成的一个 Promise 对象,而 await 命令就是内部 then 命令的语法糖。

async function asyncPrint() {
        await new Promise((resolve) => {
            setTimeout(()=>{
                resolve(2);
                console.log(1);
            }, 1000);
        })
        console.log(2);
    }
    asyncPrint();// 1 2

实现原理

async 异步是由 generator 函数 + Promise 函数实现的。
以上 asyncPrint 方法在 babel 官网 解析成 ES5 语法如下:

    "use strict";

    function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
        try {
            var info = gen[key](arg);
            var value = info.value;
        } catch (error) {
            reject(error);
            return;
        }
        if (info.done) {
            resolve(value);
        } else {
            Promise.resolve(value).then(_next, _throw);
        }
    }

    function _asyncToGenerator(fn) {
        return function () {
            var self = this, args = arguments;
            return new Promise(function (resolve, reject) {
                var gen = fn.apply(self, args);

                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 asyncPrint() {
        return _asyncPrint.apply(this, arguments);
    }

    function _asyncPrint() {
        _asyncPrint = _asyncToGenerator(
                /*#__PURE__*/
            regeneratorRuntime.mark(function _callee() {
                return regeneratorRuntime.wrap(function _callee$(_context) {
                    while (1) {
                        switch (_context.prev = _context.next) {
                            case 0:
                                _context.next = 2;
                                return new Promise(function (resolve) {
                                    setTimeout(function () {
                                        resolve(2);
                                        console.log(1);
                                    }, 1000);
                                });

                            case 2:
                                console.log(2);

                            case 3:
                            case "end":
                                return _context.stop();
                        }
                    }
                }, _callee);
            }));
        return _asyncPrint.apply(this, arguments);
    }

    asyncPrint();

转换解析

以上代码中的 regeneratorRuntime 方法是 facebook 提供的 regenerator-runtime 模块中的。是提供生成器函数、async、await 函数经 babel 编译后的功能实现。模块内容可直接下载或百度。

  1. 以上代码中 regeneratorRuntime.mark 是将它的参数(参数是一个函数)转换为 generator 函数的函数。

regenerator-runtime 模块部分源码:

    // 将传参genFun函数封装成生成器函数"GeneratorFunction",具体细节是
    //    __proto__属性设置为GeneratorFunctionPrototype,[toStringTagSymbol]属性设为"GeneratorFunction"
    //    prototype属性设为Gp对象(迭代器原型),即Generator的prototype的原型,使genFun instanceof Generator返回真值
    // 注:instanceof方法用于判断对象是否某构造函数的实例,就其本质通过判断该对象是否由构造函数的原型对象Object.create创建
    runtime.mark = function(genFun) {
        if (Object.setPrototypeOf) {
            Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
        } else {
            genFun.__proto__ = GeneratorFunctionPrototype;
            if (!(toStringTagSymbol in genFun)) {
                genFun[toStringTagSymbol] = "GeneratorFunction";
            }
        }
        genFun.prototype = Object.create(Gp);
        return genFun;
    };
  1. 以上代码中 _asyncToGenerator 返回了一个匿名函数,该匿名函数中返回一个 Promise 对象。
  2. 在该 Promise 对象中 gen 是 generator 函数的第一次调用。
  3. 继续调用 Promise 中的 _next 方法,_next 方法中又调用 asyncGeneratorStep 方法。
  4. asyncGeneratorStep 方法中,变量 info 是第一次调用 next 得到的值。然后返回 Promise.resolveresolve 中的参数也是一个 Promise。这一轮走了 _callee$ 方法中 switch 中的 case 0。即打印出 1 。
  5. 继续 4-5 步,打印出 2。
  6. 直到 info{value:undefined,done:true} ,即执行完成。

总结

所以 async 异步函数就是通过 generator 函数的 next 方法控制一步步执行,然后每次执行完都返回一个 Promise 对象。

发布了280 篇原创文章 · 获赞 2497 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/weixin_44135121/article/details/104434645