JavaScript深刻浅出异步编程3、async、await

前面两篇文章已经分别分析了setTimeoutsetIntervalPromise了,可是提到异步编程,还有一个没有分析,那就是ES8的asyncawait。然而提到asyncawait就不得不说yieldgenerator。下面就一一分析javascript

asyncawait

照例,在开始以前先举例子。下面先定义两个异步方法。java

function func1() {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve('func1 result');
    }, 1000);
  });
}

function func2() {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve('func2 result');
    }, 1000);
  });
}
复制代码

在实际的开发中,两个异步方法有可能须要串行执行,在没有asyncawait以前那么直接使用promisethen的链式调用来实现。好比:ajax

func1().then(function(result1){
    console.log(result1);
    return func2();
}).then(function(result2){
    console.log(result2);
});
复制代码

上面的代码等func1执行完毕后打印结果,而后继续执行func2编程

而后如今有了asyncawait后就能够改用下面的代码来写了:promise

// 串行执行两个异步方法
async function funcAysnc(){
    var result1 = await func1();
    console.log(result1);
    var result2 = await func2();
    console.log(result2);
}
funcAysnc();
复制代码

两种实现方式,明显第二种看起来舒服多了。看起来真的像是在串行执行两个异步的代码。可是这实际上是假象,asyncawait说白了就是语法糖,咱们看下babel编译后的代码,先将yieldgenerator编译插件关了,转码获得以下代码:babel

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 funcAysnc() {
    return _funcAysnc2.apply(this, arguments);
}
function _funcAysnc2() {
    _funcAysnc = _asyncToGenerator(
        function* _callee() {
            var result1 = yield func1();
            console.log(result1);
            var result2 = yield func2();
            console.log(result2);
        }
    );
    return _funcAysnc.apply(this, arguments);
}

funcAysnc();
复制代码

你会看到,babelasyncawait转码成generatoryield了。而generatoryield又属于ES6的规范,那么也就是说,实际上ES6自己就能够支持asyncawait架构

下面继续剖析generatoryieldapp

generatoryield

如今把babelyield插件打开。而后看下编译代码:异步

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

function _funcAysnc() {
  _funcAysnc = _asyncToGenerator(regeneratorRuntime.mark(function _callee() {
    var result1, result2;
    return regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.next = 2;
            return func1();

          case 2:
            result1 = _context.sent;
            console.log(result1);
            _context.next = 6;
            return func2();

          case 6:
            result2 = _context.sent;
            console.log(result2);

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

funcAysnc();
复制代码

你会发现,babelyieldgenerator进一步的进行了转码。可是若是你如今直接运行的话你会发现,没法运行。报regeneratorRuntime is not defined这样的错误,也就是说没有找到regeneratorRuntime这个对象,这说明什么?说明generator好像并不能直接经过转码直接运行,若是你百度下的话,人家会告诉你须要为babel添加transform-runtime这个插件从新编译。添加后确实能运行了,可是transform-runtime作了什么呢?咱们可否直接写一个regeneratorRuntime呢?答案固然能够,并且实现起来也很简单,下面直接贴出regeneratorRuntime实现代码。async

var regeneratorRuntime = new function () {
    function Context() {
        this.prev = 0;
        this.next = 0;
        this.sent = null;
        this.isDone = false;
        this.abrupt = function (op, value) {
            this.isDone = true;
            if (op.toString() === 'return') {
                return value;
            }
            return undefined;
        }

        this.stop = function () {
            this.isDone = true;
            return undefined;
        }
    }
    function RegeneratorYield(func, target) {
        _context = new Context();
        this.next = function (sent) {
            var result = null;
            _context.sent = sent;
            if (_context.isDone) {
                result = undefined;
            } else {
                result = func.call(target, _context);
            }
            _context.sent = null;
            return { value: result, done: _context.isDone };
        }
    }
    this.mark = function (func) {
        return func;
    }
    this.wrap = function (func, mark, target) {
        return new RegeneratorYield(func, target);
    }
}
复制代码

从上面的代码能够看出,无论是asyncawait仍是yieldgenerator,都只是语法糖,通过babel的转码,都会转成ES5的代码。哪怕generator稍微有点特殊,可是generator自己的原理并不复杂,哪怕本身写一个出来均可以。下一篇,我会单独写一篇关于generator的原理剖析。详细的介绍yieldgenerator的实现原理。

另外,咱们也能够从转码后的代码看出,asyncawait自己也并无直接提供异步编程的能力。仅仅只是个语法糖而已,都是假象。

最后

这里花了三篇文章着重介绍了在JavaScript中进行异步编程的方法以及背后原理,咱们会发现,咱们平时开发时候经常使用的Promiseasyncawait自己压根就没有具有异步的功能,都是假象。非要说具有异步能力的API,那也就剩下setTimeoutsetIntervalXMLHttpRequest(ajax)了。

可是哪怕这些是假象,可是在咱们平时的开发过程当中确实能给咱们的开发体验带来质的改变,甚至可以直接影响到整个项目的架构设计。

同时也以为javascript是一个很奇妙的语言,有很大的潜力,几乎无所不能。

相关文章
相关标签/搜索