前面两篇文章已经分别分析了setTimeout
、setInterval
、Promise
了,可是提到异步编程
,还有一个没有分析,那就是ES8的async
、await
。然而提到async
、await
就不得不说yield
、generator
。下面就一一分析javascript
async
和await
照例,在开始以前先举例子。下面先定义两个异步方法。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);
});
}
复制代码
在实际的开发中,两个异步方法有可能须要串行执行
,在没有async
、await
以前那么直接使用promise
的then
的链式调用来实现。好比:ajax
func1().then(function(result1){
console.log(result1);
return func2();
}).then(function(result2){
console.log(result2);
});
复制代码
上面的代码等func1
执行完毕后打印结果,而后继续执行func2
。编程
而后如今有了async
、await
后就能够改用下面的代码来写了:promise
// 串行执行两个异步方法
async function funcAysnc(){
var result1 = await func1();
console.log(result1);
var result2 = await func2();
console.log(result2);
}
funcAysnc();
复制代码
两种实现方式,明显第二种看起来舒服多了。看起来真的像是在串行执行两个异步的代码。可是这实际上是假象,async
和await
说白了就是语法糖,咱们看下babel
编译后的代码,先将yield
和generator
编译插件关了,转码获得以下代码: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();
复制代码
你会看到,babel
将async
和await
转码成generator
和yield
了。而generator
和yield
又属于ES6
的规范,那么也就是说,实际上ES6自己就能够支持async
和await
。架构
下面继续剖析generator
和yield
。app
generator
和yield
如今把babel
的yield
插件打开。而后看下编译代码:异步
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();
复制代码
你会发现,babel
对yield
和generator
进一步的进行了转码。可是若是你如今直接运行的话你会发现,没法运行。报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);
}
}
复制代码
从上面的代码能够看出,无论是async
、await
仍是yield
、generator
,都只是语法糖,通过babel
的转码,都会转成ES5
的代码。哪怕generator
稍微有点特殊,可是generator
自己的原理并不复杂,哪怕本身写一个出来均可以。下一篇,我会单独写一篇关于generator
的原理剖析。详细的介绍yield
和generator
的实现原理。
另外,咱们也能够从转码后的代码看出,async
和await
自己也并无直接提供异步编程
的能力。仅仅只是个语法糖而已,都是假象。
这里花了三篇文章着重介绍了在JavaScript
中进行异步编程
的方法以及背后原理,咱们会发现,咱们平时开发时候经常使用的Promise
、async
、await
自己压根就没有具有异步
的功能,都是假象。非要说具有异步
能力的API,那也就剩下setTimeout
、setInterval
、XMLHttpRequest(ajax)
了。
可是哪怕这些是假象,可是在咱们平时的开发过程当中确实能给咱们的开发体验带来质的改变,甚至可以直接影响到整个项目的架构设计。
同时也以为javascript
是一个很奇妙的语言,有很大的潜力,几乎无所不能。