前面兩篇文章已經分別分析了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
是一個很奇妙的語言,有很大的潛力,幾乎無所不能。