文章同步自個人博客:http://www.52cik.com/2016/07/11/generator co.html 此前只是簡單使用而沒有真正的去研究 Generator,這次要好好折騰下這貨。 <! more 非同步編程 對於 jser 來說,非同步非常熟悉了吧,但是真正理解非同步的卻不多,因為大部 ...
此前只是簡單使用而沒有真正的去研究 Generator,這次要好好折騰下這貨。
非同步編程
對於 jser 來說,非同步非常熟悉了吧,但是真正理解非同步的卻不多,因為大部分人只知道回調。
隨著js的快速發展,非同步方案也層出不窮,從最開始的回調到Promise,再到Generator,然後到async/await。
甚至有人說 async/await 是非同步的終極解決方案,我不敢直接贊同,只能說是目前最好的非同步體驗。
本篇先從 Generator 講起,後序再詳細說 async/await。
從回調開始
從最最經典的 ajax 請求開始今天的話題吧。
假如,我們要依次請求 url1, url2, url3 這3個地址。
$.get('url1', function(r1) {
$.get('url2', function(r2) {
$.get('url3', function(r3) {
console.log(r1, r2, r3);
});
});
});
一不小心就寫成這樣了。
如果你是 jQuery 粉的話,你可能會說也可以這樣實現啊。
$.get('url1').then(function(r1) {
console.log(r1);
return $.get('url2');
}).then(function(r2) {
console.log(r2);
return $.get('url3');
}).then(function(r3) {
console.log(r3);
});
用 jQuery 的 Deferred 對象,類似 Promise 來規避回調地獄,看著確實平了,但體驗並不是特別友好。
用 Generator 來和諧回調
Generator 的基礎這裡就不展開說了,直接說應用。
function* gen() {
var r1 = yield $.get('url1');
var r2 = yield $.get('url2');
var r3 = yield $.get('url3');
console.log(r1, r2, r3);
}
這是比較友好的非同步方式,但是還有個至關重要的因素,怎麼運行這個 Generator 是個問題。
直接手動 g.next()
運行那肯定不行,鬼知道有多少個 yield。
我們要實現一個啟動器來運行它,並把 Promise 結果傳給下一次next,這樣就實現了 yield 接收值的功能。
先來實現一個最簡陋的起動器。
function run(gen) {
var g = gen();
function next(d) {
var r = g.next(d);
r.done || r.value.then(function(d){ next(d) }); // 這個是關鍵,把值傳回傳
}
next();
}
然後我們只要一行代碼。
run(gen);
Generator 就啟動起來了,並且一直執行到 done 為 true 為止。
真實例子
打開 http://www.52tian.net/ 動漫網。非廣告,確實沒找到合適的測試站,湊合下吧。
然後把下麵代碼貼到控制台,看下結果。如果執行不了,請升級瀏覽器,本例在 chrome 51 下通過。
function* gen() {
var r1 = yield $.get('/json/anime/4126.htm');
var r2 = yield $.get('/json/anime/11129.htm');
var r3 = yield $.get('/json/anime/427.htm');
console.log([r1, r2, r3].join('\n'));
}
function run(gen) {
var g = gen();
function next(d) {
var r = g.next(d);
r.done || r.value.then(function(d){ next(d) }); // 這個是關鍵,把值傳回傳
}
next();
}
run(gen);
小結
可能你已經發現了,其實這就是 co 的原理,但 co 比這個例子嚴謹多了,而且api設計的也非常友好。
本篇到此也就結束了,利用 Generator 的 yield 功能實現參數回傳,讓代碼看起來非常‘同步’,讓非同步體驗變的更加友好。