@tophallen是對的。您可以完全在ES3/ES5中實現相同的功能。但語法不盡相同。我們舉一個例子,希望解釋爲什麼語法很重要。
ES6生成器的主要應用之一是異步操作。有severalrunners被設計來包裝發電機產生的序列Promises。當包裹的發生器產生承諾時,這些跑步者等待,直到該承諾解決或被拒絕,然後恢復生成器,將結果傳回或在使用iterator.throw()
的屈服點拋出異常。
一些跑步者,如tj/co,另外允許生成承諾數組,傳回數組值。
這裏是例子。這個函數執行兩個並行的URL請求,然後解析它們的結果如JSON,組合它們以某種方式,組合數據發送至其他URL,並返回回答(一個的承諾):
var createSmth = co.wrap(function*(id) {
var results = yield [
request.get('http://some.url/' + id),
request.get('http://other.url/' + id)
];
var jsons = results.map(JSON.parse),
entity = { x: jsons[0].meta, y: jsons[1].data };
var answer = yield request.post('http://third.url/' + id, JSON.stringify(entity));
return { entity: entity, answer: JSON.parse(answer) };
});
createSmth('123').then(consumeResult).catch(handleError);
注意,這個代碼包含幾乎沒有樣板。大多數行執行上面描述中存在的一些操作。
還注意到缺少錯誤處理代碼。同步(如JSON解析錯誤)和異步(如失敗的url請求)的所有錯誤都會自動處理,並會拒絕所產生的承諾。
如果您需要從某些錯誤中恢復(即阻止它們拒絕生成的Promise),或者使它們更具體,那麼您可以將發生器中的任何代碼塊都包圍在try..catch
之內,並且同步和異步錯誤將在catch
區塊結束。
同樣可以使用的功能的陣列和一些輔助庫像async可以肯定實現:
var createSmth = function(id, cb) {
var entity;
async.series([
function(cb) {
async.parallel([
function(cb){ request.get('http://some.url/' + id, cb) },
function(cb){ request.get('http://other.url/' + id, cb) }
], cb);
},
function(results, cb) {
var jsons = results.map(JSON.parse);
entity = { x: jsons[0].meta, y: jsons[1].data };
request.post('http://third.url/' + id, JSON.stringify(entity), cb);
},
function(answer, cb) {
cb(null, { entity: entity, answer: JSON.parse(answer) });
}
], cb);
};
createSmth('123', function(err, answer) {
if (err)
return handleError(err);
consumeResult(answer);
});
但是,這實在是太醜了。更好的想法是使用承諾:
var createSmth = function(id) {
var entity;
return Promise.all([
request.get('http://some.url/' + id),
request.get('http://other.url/' + id)
])
.then(function(results) {
var jsons = results.map(JSON.parse);
entity = { x: jsons[0].meta, y: jsons[1].data };
return request.post('http://third.url/' + id, JSON.stringify(entity));
})
.then(function(answer) {
return { entity: entity, answer: JSON.parse(answer) };
});
};
createSmth('123').then(consumeResult).catch(handleError);
短,更清潔,但仍比代碼在使用發電機的版本。還有一些樣板代碼。注意這些.then(function(...) {
行和var entity
聲明:它們不執行任何有意義的操作。
較少的樣板(=生成器)使您的代碼更容易理解和修改,寫得更有樂趣。這些是任何代碼最重要的特徵之一。這就是爲什麼很多人,尤其是那些習慣了其他語言的類似概念的人,對發電機非常欣喜若狂:)
關於第二個問題:transpilers使用閉包,switch
語句和狀態對象來做他們的沉浸魔法。例如,該功能:
function* f() {
var a = yield 'x';
var b = yield 'y';
}
將regenerator轉化爲這一個(的Traceur輸出看起來非常相似):
var f = regeneratorRuntime.mark(function f() {
var a, b;
return regeneratorRuntime.wrap(function f$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
context$1$0.next = 2;
return "x";
case 2:
a = context$1$0.sent;
context$1$0.next = 5;
return "y";
case 5:
b = context$1$0.sent;
case 6:
case "end":
return context$1$0.stop();
}
}, f, this);
});
正如你所看到的,沒有什麼神奇的是,得到ES5是相當微不足道的。真正的魔力在於生成ES5的代碼,即在轉譯代碼中,因爲它們需要支持所有可能的邊緣情況。最好這樣做的結果是高性能的輸出代碼。
UPD:here is an interesting article可以追溯到2000年,並描述在純C :)是再生器和其他ES6> ES5 transpilers用於捕獲發電機的狀態的技術實現僞協同程序非常相似。
它更多的是你從代碼中看不到的東西,而不是你所做的;內部優化。 – dandavis 2015-02-08 00:59:20