[英]Difference between ES6 Generators and Array of Functions
在閱讀javascript博客和文章時,我看到了很多人對ES6 Generators感興趣,但我不明白它們與使用一系列函數構成的當前序列的本質區別。 例如,下面的工廠將采取一系列功能步驟並在步驟之間產生。
function fakeGen(funcList) {
var i = 0, context;
return function next() {
if (i<funcList.lenght) {
return {value: funcList[i++](context)}
} else return {done:true}
}
}
我錯過了什么好處以及如何在ES6中實現魔法轉換?
生成器本質上是一個枚舉器函數,它允許在調用它時更改您正在操作的上下文,實際上它與您的函數數組之間沒有太大的區別,但是您獲得的優勢是它不必是正在評估的函數內的函數,簡化了閉包。 請看以下示例:
function* myGenerator() {
for (var i = 0; i < arr.length; i++) {
yield arr[i];
}
}
這是一個非常簡單的示例,但不是必須構建您需要為某人枚舉結果提供的上下文,它是為您提供的,並確保在done
之前done
屬性將為false。 此功能看起來比您給出的示例更清晰。 可能最大的優勢在於圍繞此優化可以在引擎蓋下進行,因此優化了對象內存占用。
一個很好的接觸是,在枚舉多個對象集合時,您真正清理代碼,如下所示:
function* myGenerator() {
for (var i = 0; i < arr.length; i++) {
yield arr[i];
}
for (var i = 0; i < arr2.length; i++) {
yield arr2[i];
}
yield* myGenerator2();
}
使用鏈接的嵌套函數執行此操作可以完成相同的任務,但代碼的可維護性和可讀性會受到一些影響。
就轉發器而言,來自CS線程:
沒有沖突。 Coffeescript將生成編譯它使用的任何語法所需的任何javascript,無論是舊的還是新的。
過去,在所有瀏覽器都支持之前,coffeescript不會使用任何javascript功能。 這也可能適用於發電機。 在此之前,您需要使用反引號。
我對大多數編譯器的一般理解是,在實現不會遍歷並且通常兼容的功能時,他們必須要小心,因此通常在游戲后期。
就像你說的那樣,生成器沒有做任何超級特殊的事情,它只是語法糖,使編碼更容易閱讀,維護,消費或表現更好。
@tophallen是對的。 您可以完全在ES3 / ES5中實現相同的功能。 但語法不一樣。 讓我們舉一個例子,希望能解釋為什么語法很重要。
ES6生成器的主要應用之一是異步操作。 有幾個 跑步者設計用於包裹生成一系列Promise的生成器。 當一個包裝的生成器產生一個promise時,這些runners會等到Promise被解析或拒絕,然后恢復生成器,傳回結果或使用iterator.throw()
在yield點拋出一個異常。
一些運行器,如tj / co ,還允許產生promises數組,傳回值數組。
這是一個例子。 此函數並行執行兩個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(...) {
lines和var entity
聲明:它們不執行任何有意義的操作。
較少的樣板(=生成器)使您的代碼更易於理解和修改,並且編寫起來更有趣。 這些是任何代碼最重要的特征之一。 這就是為什么許多人,特別是那些習慣於其他語言中的類似概念的人,對發電機如此欣喜若狂:)
關於你的第二個問題:轉發器使用閉包, switch
語句和狀態對象來完成他們的魔術。 例如,這個功能:
function* f() {
var a = yield 'x';
var b = yield 'y';
}
將由再生器轉換為這個( 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 : 這是一篇有趣的文章 ,可以追溯到2000年,描述了在純C中偽協同程序的實現:) Regenerator和其他ES6> ES5轉換器用於捕獲生成器狀態的技術非常相似。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.