Node.js 中使用 Promise Q

假設我們要執行一項工作 task 3,但執行 task 3 之前要先依序完成工作 task 2 與 task 1。如果沒有使用 promise,而用 callback 的方式完成,程式碼大概會是這樣…

task1(function (value1) {
  task2(value1, function(value2) {
    task3(value2, function(value3) {
      // do something with value3...
    });
  });
});

要做(依賴)的事情愈多,深度就愈深,完全就是 callback hell 了。

使用 Promise

承上,若使用 promise,只要等到目前的工作(task 1)完成,回傳一個訊息告知下一個工作(task 2) 可以開始動工,就解決這個 callback hell 的問題了。

var Q = require('q'), deferred = Q.defer();

function task1(value1) {
  var response = "Complete task with " + value1;
  deferred.resolve(response);
  return deferred.promise;
}

function task2(value2) {
  console.log(value2);
}

task1('Hello World').then(function(success) {
  task2(success);
},function(error){
  console.log(error);
},function(progress){
  console.log(progress);
})

注意,回傳的 promise 有三種狀態

為了舉例方便,範例只呈現了回傳成功的狀態。

結果

Complete task with Hello World

改寫前面依序執行三個工作(task 1、task 2與task 3)的 callback hell。

function task1(value){
  var deferred = Q.defer(),
      response = "Complete task" + value + " with Hello World" + value;
  deferred.resolve(response);
  return deferred.promise;
}

function task2(value){
  var deferred = Q.defer(),
      response = "Complete task" + value + " with Hello World" + value;
  deferred.resolve(response);
  return deferred.promise;
}

function task3(value){
  var deferred = Q.defer(),
      response = "Complete task" + value + " with Hello World" + value;
  deferred.resolve(response);
  return deferred.promise;
}

var promise1 = task1('1'), promise2, promise3;

promise2 = promise1.then(function(res) {
  console.log(res);
  return task2('2');
})

promise3 = promise2.then(function(res) {
  console.log(res);
  return task3('3');
})

promise3.then(function(res) {
  console.log(res);
})

promise2 = promise1.then(function(res) {
  console.log(res);
  return task2('2');
})

簡化

由於 return 的值都是 promise,因此可以簡化成以下的樣子。

task1('1')
.then(function(res) {
  console.log(res);
  return task2('2');
})
.then(function(res) {
  console.log(res);
  return task3('3');
})
.done(function(res){
  console.log(res);
});

結果

Complete task1 with Hello World1
Complete task2 with Hello World2
Complete task3 with Hello World3

Q.all[]

但若要等待多個工作呢?例如:等待 task 1 與 task 2(任意順序)完成後,再繼續完成 task 3。

var Q = require('q');

function task1(value1){
  var response = "Complete task with " + value1,
      deferred = Q.defer();

  deferred.resolve(response);
  return deferred.promise;
}

function task2(value2){
  var response = "Complete task with " + value2,
      deferred = Q.defer();

  deferred.resolve(response);
  return deferred.promise;
}

function task3(result1, result2) {
  console.log(result1);
  console.log(result2);
}

var value1 = "Hello",
    value2 = "World",
    promise = Q.all([task1(value1), task2(value2)]);

promise.then(function(results){
  task3(results[0], results[1]);
})

將 taks 1 和 task 2 放到 Q.all[] 中,當 task 1 或 task 2 完成時,會回傳 promise。當 task 1 與 task 2 都回傳 promise 時,就會經由 promise.then() 執行接下來的工作,也就是 task 3。

結果

Complete task with Hello
Complete task with World

References


這篇文章的原始位置在這裡-Node.js 中使用 Promise Q

由於部落格搬遷至此,因此在這裡放了一份,以便閱讀;部份文章片段也做了些許修改,以期提供更好的內容。

node.js promise