快樂玩 ES6 Generator,從 co 起手式開始
自從 Node.js 0.12 版和 io.js 之後,大量的開發者開始了各自的 ECMAScript 6 大冒險,許多人對 Generator 的使用仍跌跌撞撞,對於這種看似「同步(Synchronous)」的「異步(Asynchronous)」機制,有許多人腦袋遲遲無法轉過來。雖然在小弟的書(參閱連結:新書報到!Node.js 模組參考手冊!)已經有清楚的說明 Generator 使用方法,但就許多讀者回函來看,對於 JavaScript 越是熟悉的人,越無法直觀理解 Generator 的思維,甚至是老是抓不准使用的時機點。
尤其是過去我們已經有 Promise、async、Q 和 bluebird 等處理非同步程式流程的模組和工具,很多人就是覺得沒有使用 Generator 的必要。不過,如果你會使用 co 模組,你會突然發現若是將過去的流程機制與 Generator 相搭配,程式開發將變得更為流暢。
若想要安裝 co 模組,可以直接以 NPM 下載:
npm install co
本文接下來會說明一些 co 的基本使用,破除一些 Generator 難以使用的地方,讓開發者們更容易開始 Generator 的旅程。
讓原生 Generator 更好用
這是很多人不喜歡使用 Generator 的主因,以往為了使用 Generator,我們還要先建立一個 Generator 的函數,然後不時的去處理 Generator 所返回的資訊,一遍又一遍進入 Generator 之中。因此,無論 Generator 再好用,這些麻煩的動作也完全抵銷他的優勢,大多數人還不如回到舊的流程控制方法,以免徒增自己的麻煩。而如果使用 co,可以直接將 Generator 函數當作「立即函數使用」,其餘的部份我們可以不需要擔心:
var co = require('co'); co(function *() { console.log('Inside'); })
再也不用煩惱 yield!
以前光是 Promise,就已經讓很多人詬病,覺得每次使用 Promise 都要花許多時間對函數進行包裝,而 Generator 也有類似的問題,若是要使用 yield,更是一件大工程。於是,co 幫開發者做了些設計,讓 Generator 可以直接利用 yield 去跑 Promise 類型的函數、陣列等物件,因為是 yield,其執行模式是「異步」,不會阻塞事件引擎。co 支援 Trunks,也就是回傳一個簡單函數進行異步工作,範例如下:
var co = require('co'); function updateInfo() { // 返回一個將被執行的函數 return function(done) { // 可以執行各種異步工作 setTimeout(function() { // 完成工作後執行 callback,參數一為錯誤訊息,參數二為回傳值 done(null, 'Done'); }, 1000); }; } co(function *() { // 執行並等待回傳值 var ret = yield updateInfo(); // 一秒後收到回傳值並印出來 console.log(ret); });
支援 Promise 的 yield
如果你熟悉 Promise,這是個好消息, co 讓 yield 可以直接吃 Promise:co(function *() { var ret = yield Promise.resolve(true); });
直接使用 yield 跑一整個陣列的工作
前面提到 co 讓 yield 也支援陣列,所以我們可以準備一系列的工作,放在陣列中讓 yield 去處理:co(function *() { var a = Promise.resolve(true); var b = Promise.resolve(true); var c = Promise.resolve(true); var ret = yield [ a, b, c ]; });
異步處理一個陣列裡的所有元素
這大概是以往 JavaScript 最頭痛的工作,後來有了 async 這類模組後,才比較容易處理,不然光使用 forEach 或是 for 迴圈來做,碰到較大的陣列,往往使程式阻塞卡死。如果使用 co 前面的起手式,我們可以試著這樣做(如果不使用 Promise):var arr = [ 1, 2, 3, 4, 5 ]; function handle(el) { return function() { // 處理傳入值 console.log(el); }; } co(function *() { // 在 Generator 裡,使用 yield 不會使 JavaScript 事件引擎阻塞, // 但因為程式流程會等 yield 結束,for 迴圈也不會造成阻塞 for (var index in arr) { var el = arr[index]; yield handle(el); } });
留言
張貼留言