1. 程式人生 > 程式設計 >JS co 函式庫的含義和用法例項總結

JS co 函式庫的含義和用法例項總結

本文例項講述了JS co 函式庫的含義和用法。分享給大家供大家參考,具體如下:

繼續學習阮一峰老師非同步程式設計四部曲之三:co

co在很早之前就聽超哥講過,說在node程式設計中大量用到,原始碼很簡單,但是想法很強大。

讓我有空抓緊了解下,前一段時間弄離職的事情,跑來跑去累的夠嗆。

現在終於一切迴歸正常了,還在拼命的適應新公司的節奏。只能趁週末繼續學習了。

好了,不瞎扯了,迴歸主題,前兩篇文章我們分別學習了 Generator 函式和 Thunk 方式的自動執行。

今天我們接著上次的思路學習使用 co 工具實現 Generator 函式的自動執行。

再次拿出上一節的示例:

var fs = require('fs');
var thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);
 
var gen = function* (){
 var r1 = yield readFile('/etc/fstab');
 console.log(r1.toString());
 var r2 = yield readFile('/etc/shells');
 console.log(r2.toString());
};

這段程式碼用於讀取兩個檔案,怎麼使用 co 工具來實現自動執行呢?

很簡單:

var co = require('co');
co(gen);

上面程式碼中,只要把 Generator 函式傳入 co 函式,就會自動執行。

co 函式返回一個 Promise 物件,因此可以用 then 方法添加回調函式,當 Generator 執行結束就會觸發回撥:

co(gen).then(function (){
 //success
});

這麼看起來的話好像和我們之前的 Thunk 方式差不多啊,只是把 run 函式名改成了 co

唯一的區別是多了一層 Promise 包裝,那麼 co 和 Thunk 到底有什麼區別呢?

正如我們的猜測一樣,co 其實是將之前的兩種自動執行方式(Thunk 和 Promise)結合到了一起,包裝成一個庫。

使用它的前提和比Thunk多了一種 Promise 的情況,也就是yield返回值必須是 Thunk 函式和 Promise 物件之一

實現原理:

之前我們在研究 Thunk 函式時的第一步是使用 thunkify 將 readFile 包裝成 Thunk 函式:

今天我們研究 Promise 就需要把 readFile 包裝成 Promise 物件,看程式碼:

var fs = require('fs');
 
var readFile = function (fileName){
 return new Promise(function (resolve,reject){
  fs.readFile(fileName,function(error,data){
   if (error) reject(error);
   resolve(data);
  });
 });
};
 
var gen = function* (){
 var f1 = yield readFile('/etc/fstab');
 var f2 = yield readFile('/etc/shells');
 console.log(f1.toString());
 console.log(f2.toString());
};

接下來是我特別喜歡老師做的一件事,先用最原始的方式,手動執行一遍程式碼。

在我們日常開發的過程中,也可以參考這種方式。當邏輯複雜時,不妨先用最直白的方式把它實現,

然後再去發現裡面的規則,去優化重構。又開始扯了,看一下手動執行的程式碼:

var g = gen();
 
g.next().value.then(function(data){
 g.next(data).value.then(function(data){
  g.next(data);
 });
});

手動執行其實就是用 then 方法,層層添加回調函式。理解了這一點,就可以寫出一個自動執行器:

function run(gen){
 var g = gen();  //開始執行
 
 function next(data){
  var result = g.next(data);
  if (result.done) return result.value;
  result.value.then(function(data){
   next(data);
  });
 }
 next();
}
run(gen);

和Thunk函式的區別是,Thunk 函式在執行成功後把 next 傳給 thunkify ,讓 thunkify 來幫忙執行 next

這裡的做法是吧 next 交給 Promise,promise 控制請求成功時執行 next。區別只有這麼一點。

分析了原理之後,我們來分析下co的原始碼:

co 函式接受一個 Generator 引數,返回一個 Promise 物件

function co(gen) {
 var ctx = this;
 
 return new Promise(function(resolve,reject) {
 });
}

在返回的 Promise 物件裡面,先檢查引數 gen 是否為 Generator 函式。如果是,就執行該函式,得到一個內部指標物件;

如果不是就返回,並將 Promise 物件的狀態改為 resolved 。

function co(gen) {
 var ctx = this;
 
 return new Promise(function(resolve,reject) {
  if (typeof gen === 'function') gen = gen.call(ctx);
  if (!gen || typeof gen.next !== 'function') return resolve(gen);
 });
}

接著,co 對 next 方法進行包裝,使異常能夠暴露出來:

function co(gen) {
 var ctx = this;
 
 return new Promise(function(resolve,reject) {
  if (typeof gen === 'function') gen = gen.call(ctx);
  if (!gen || typeof gen.next !== 'function') return resolve(gen);
 
  onFulfilled();
  function onFulfilled(res) {
   var ret;
   try {
    ret = gen.next(res);
   } catch (e) {
    return reject(e);
   }
   next(ret);
  }  
 });
}

最後就是next方法了:

function next(ret) {
 if (ret.done) return resolve(ret.value);
 var value = toPromise.call(ctx,ret.value);
 if (value && isPromise(value)) return value.then(onFulfilled,onRejected);
 return onRejected(new TypeError('You may only yield a function,promise,generator,array,or object,'
    + 'but the following object was passed: "' + String(ret.value) + '"'));
  }
});

next方法最主要的一行:

if (value && isPromise(value)) return value.then(onFulfilled,onRejected);

co的使用條件,每一次yield返回必須是Promise物件,當promist處理成功時再次執行onFulfilled

以此來達到自動執行的效果。

co 還支援併發的非同步操作,yield 返回一個數組或者是支援遍歷的物件即可:

// 陣列的寫法
co(function* () {
 var res = yield [
  Promise.resolve(1),Promise.resolve(2)
 ];
 console.log(res); 
}).catch(onerror);
 
// 物件的寫法
co(function* () {
 var res = yield {
  1: Promise.resolve(1),2: Promise.resolve(2),};
 console.log(res); 
}).catch(onerror);

至此,co 的自動執行原理我們已經學習完成了,

其實本質上不是很難,只是涉及到的模組較多,思路比較靈活

對我的小容量大腦來說,現在的認識還只到90%

所以這篇文章裡面自己的思想比較少,更多的還是在複述老師的思路。

最後貼上原文的地址:co 函式庫的含義和用法

感興趣的朋友可以使用線上HTML/CSS/JavaScript程式碼執行工具:http://tools.jb51.net/code/HtmlJsRun測試上述程式碼執行效果。

更多關於JavaScript相關內容可檢視本站專題:《JavaScript常用函式技巧彙總》、《javascript面向物件入門教程》、《JavaScript錯誤與除錯技巧總結》、《JavaScript資料結構與演算法技巧總結》及《JavaScript數學運算用法總結》

希望本文所述對大家JavaScript程式設計有所幫助。