q

q模組是實作Promises/A+ Spec規範的一個模組,主要目的是透過豐富的流程語意來定義非同步的流程操作。其他實作promises套件還有像when.js等,可參考:http://wiki.commonjs.org/wiki/Promises/A。

官方網站

https://github.com/kriskowal/q

Installation

npm install q

Sample Usage

下面範例展示基本的q操作方式,透過request module的callback function,讓延遲效果看起來比較有感(因為每個網站在每個request下,回應時間其實是不固定的):

var Q = require('q')
  , request = require('request')
  , queue = ['http://www.google.com','http://micloud.tw','http://tw.yahoo.com'];

var fn = function(url) {
  var deferred = Q.defer();
  request.get(url, function(e,r,d){
    if(e) console.log(e);
    console.log('[%s] word count:%s', url, d.length);
    deferred.resolve();
  });
  return deferred.promise;
};

//透過allResolved,將傳入的function執行完成後,才會進入.then()
Q.allResolved([ fn(queue[0]), fn(queue[1]), fn(queue[2]) ])
  .then(function(){
    console.log('end...')
  })
  .done();

範例結果:

$ node sample01
[http://www.google.com] word count:44919
[http://tw.yahoo.com] word count:301355
[http://micloud.tw] word count:37213
end...

上面範例中,其實每個被傳入的function都實際被執行完,但是因為使用request module,造成回傳時間延遲不一致,在allowResolved中的各個function實際回傳結果仍然順序與預期不一致,但是在then中的function就會保證在之後執行!

在sequencial的程式中,另外個重要的課題是將數個Job完成後,在進行整體的回傳值計算動作,在q中,可以透過spread()來做到這件事情:

var Q = require('q')
  , request = require('request')
  , queue = ['http://micloud.tw','http://www.google.com','http://tw.yahoo.com'];

var fn = function(url) {
  //在function中定義Q.defer()
  var deferred = Q.defer();
  //console.log('start of %s', url);
  request.get(url, function(e,r,d){
    if(e) console.log(e);
    console.log('[%s] word count:%s', url, d.length);
    //如果事件完成,則執行resolve()
    deferred.resolve(d.length);
  });
  return deferred.promise;
};

var out = function (x, y, z) {
  var d = Q.defer();
  console.log('x:%s, y:%s, z:%s', x, y, z);
  d.resolve();
  return d.promise;
};

//透過allResolved,將傳入的function執行完成後
//再使用spread()擷取回傳值,然後才會進入.then()
Q.allResolved([fn(queue[0]), fn(queue[1]), fn(queue[2])])
  .spread(out)
  .then(function(){
    console.log('end...');
  })
  .done();

範例結果:

$ node sample02
[http://www.google.com] word count:44919
[http://tw.yahoo.com] word count:302985
[http://micloud.tw] word count:37213
x:37213, y:44919, z:302985
end...

下面是一個更複雜的範例,可以知道q在流程上的控制,如果不是透過擷取callback,仍是只有控制啟動時間的效果...

var Q = require("q");

var oneA = function () {
  var d = Q.defer();
  var timeUntilResolve = 1500; //預期比oneB更晚結束
    console.log('1A Starting');
    setTimeout(function () {
      console.log('1A Finished');
      d.resolve('1ATime: ' + timeUntilResolve);
    }, timeUntilResolve);
    return d.promise;
};

var oneB = function () {
  var d = Q.defer();
  var timeUntilResolve = 1000;
  console.log('1B Starting');
  setTimeout(function () {
    console.log('1B Finished');
    d.resolve('1BTime: ' + timeUntilResolve);
  }, timeUntilResolve);
  return d.promise;
};

// This fuction throws an error which later on we show will be handled
var two = function (oneATime, oneBTime) {
  var d = Q.defer();
  console.log('OneA: ' + oneATime + ', OneB: ' + oneBTime);
  console.log('2 Starting and Finishing, so 3A and 3B should start');
  d.resolve();
  return d.promise;
};

var threeA = function () {
    var d = Q.defer();
    console.log('3A Starting');
    setTimeout(function () {
    console.log('3A Finished');
    d.resolve();
    }, Math.floor((Math.random()*2000)+1));
    return d.promise;
};

var threeB = function () {
    var d = Q.defer();
    console.log('3B Starting');
    setTimeout(function () {
    console.log('3B Finished');
    d.resolve();
    }, Math.floor((Math.random()*5000)+1));
    return d.promise;
};

var four = function () {
  console.log('Four is now done');
};

Q.allResolved([ oneA(), oneB() ])
.spread(two)
.then(function () { return Q.all([ threeA(), threeB() ]); })
.then(four)
.done();

此範例可以明顯看到promise.allResolved相似的實作,其實只能保證觸發的時間,透過spread與then來操控順序才是正確。

範例結果:

$ node sample03
1A Starting
1B Starting
1B Finished
1A Finished
OneA: 1ATime: 1500, OneB: 1BTime: 1000
2 Starting and Finishing, so 3A and 3B should start
3A Starting
3B Starting
3A Finished
3B Finished
Four is now done

results matching ""

    No results matching ""