NodeJS的Promise的用法

分類:IT技術 時間:2016-10-13

Javascript的特點是異步,javascript不能等待,如果你實現某件需要等待的事情,你不能停在那裏一直等待結果回來,相反,底線是使用回調callback: 你定義一個函數,這個函數只有等到結果可用時才能被調用

這種回調模型對於好的代碼組織是沒有問題的,但是也可以通過從原始回調切換到promise解決很多問題,將promise看成是一個標準的數據容器,這樣會簡化你的代碼組織,可以成為基於promise的架構。

什麽是Promise?

一個promise是一個帶有”.then()”方法的對象,其代表的是一個操作的結果可能還沒有或不知道,無論誰訪問這個對象,都能夠使用”.then()”方法加入回調等待操作出現成功結果或失敗時的提醒通知,。

那麽為什麽這樣做好處優於回調呢?標準的回調模式在我們處理請求時需要同時提供回調函數:


request(url, function(error, response) { 
  // handle success or error.
});
doSomethingElse(); 

很不幸,這段代碼意味著這個request函數並不知道它自己什麽時候能夠完成,當然也沒有必要,我們最終通過回調傳遞結果。這會導致多個回調形成了嵌套回調,或者稱為回調陷阱。


queryThedatabase(query, function(error, result) { 
  request(url, function(error, response) {
    doSomethingElse(response, function(error, result) {
      doAnotherThing(result, function(error, result) {
        request(anotherUrl, function(error, response) {
          ...
        });
      });
    });
  });
});

Promise能夠解決這種問題,允許低層代碼創建一個request然後返回一個對象,其代表著未完成的操作,讓調用者去決定應該加入什麽回調。

Promise是什麽?

promise是一個異步編程的抽象,它是一個返回值或拋出exception的代理對象,一般promise對象都有一個then方法,這個then方法是我們如何獲得返回值(成功實現承諾的結果值,稱為fulfillment)或拋出exception(拒絕承諾的理由,稱為rejection),then是用兩個可選的回調作為參數,我們可以稱為onFulfilled和OnRejected:


var promise = doSomethingAync()
promise.then(onFulfilled, onRejected)

當這個promise被解決了,也就是異步過程完成後,onFulfilled和OnRejected中任何一個將被調用,

因此,一個promise有下面三個不同狀態:

pending待承諾 – promise初始狀態
fulfilled實現承諾 – 一個承諾成功實現狀態
rejected拒絕承諾 – 一個承諾失敗的狀態

以讀取文件為案例,下面是使用回調實現讀取文件後應該做什麽事情(輸出打印):


readFile(function (err, data) {
  if (err) return console.error(err)
  console.log(data)
})

如果我們的readFile函數返回一個promise,那麽我們可以如下實現同樣的邏輯(輸出打印):


var promise = readFile()
promise.then(console.log, console.error)

這裏我們有了一個值promise代表的是異步操作,我們能夠一直傳遞這個值promise,任何人訪問這個值都能夠使用then來消費使用它,無論這個值代表的異步操作是否完成或沒有完成,我們也能保證異步的結果不會改變,因為這個promise代表的異步操作只會執行一次,狀態是要麽fulfilled要麽是rejected。

理解Promise

Promise可能是不同於日常直覺,為了理解它,一些重要原理必須記牢: .then()總是返回一個新的promise.,如下面代碼:


var promise = readFile()
var promise2 = promise.then(readAnotherFile, console.error)

這裏then的參數readAnotherFile, console.error是代表異步操作成功後的動作onFulfilled或失敗後的動作OnRejected,也就是說,讀取文件成功後執行readAnotherFile函數,否則失敗打印記錄錯誤。這種實現是兩個中只有一種可能。

我們再看下面上述代碼如下:


var promise = readFile()
var promise2 = promise.then(function (data) {
  return readAnotherFile() // 如果readFile成功,執行readAnotherFile
}, function (err) {
  console.error(err) // 如果readFile不成功,記錄,但是還是執行readAnotherFile
  return readAnotherFile()
})
promise2.then(console.log, console.error) // readAnotherFile函數的執行結果

因為then返回一個promise,它意味著promise能夠被chain串行鏈條花,這樣能避免回調地獄:


readFile()
  .then(readAnotherFile)
  .then(doSomethingElse)
  .then(...)

Promise法則有兩部分必須分離:

(1).then()總是返回一個新的promise,每次你調用它,它不管回調做什麽,因為.then()在回調被調用之前已經給了你一個承諾promise,回調的行為只影響承諾promise的實施,如果回調返回一個值,那麽promise將使用那個值,如果這個值是一個promise,返回這個promise實施後的值給這個值,如果回調拋出錯誤,promise將拒絕錯誤。

(2)被.then()返回的promise是一個新的promise,它不同於那些.then()被調用的promise,promise長長的鏈條有時會好些隱藏這個事實,不管如何,每次.then()調用都會產生一個新的promise,這裏必須註意的是你真正需要考慮的是你最後調用.then()可能代表失敗,那麽如果你不捕獲這種失敗,那麽容易導致你的錯誤exception消失。

一些人認為.then()串聯鏈條調用很類似fluent風格,但是長長的promise鏈條會讓人迷惑,最後切分為一個個有意義的函數:


function getTasks() { 
  return $http.get('http://example.com/api/v1/tasks')
    .then(function(response) {
      return response.data;
    });
}
 
function getMyTasks() { 
  return getTasks()
    .then(function(tasks) {
      return filterTasks(tasks, {
        owner: user.username
      });
    });
}

在這個例子中,兩個函數各自獲得一個promise,攜帶了一個回調函數。

有趣的Promise

同樣的promise能夠接受任何數目的回調,當一個Promise被解決實施後,其中所有回調函數都會被調用,此外,一個promise在被解決實施後,甚至可以接受一個新的回調,這些回調完成能以正常方式被調用,這就允許我們使用回調實現簡單形式的緩存:


var tasksPromise; 
function getTasks() { 
  taskPromise = taskPromise || getTasksFromTheServer();
  return taskPromise;
}

這個案例中,getTasks()函數可以被任意次數調用,它總是返回銅牙的promise,其中函數getTasksFromTheServer()卻只是被調用一次。

轉載請註明: 快樂編程 ? NodeJS的Promise的用法


Tags: function request promise success result

文章來源:


ads
ads

相關文章
ads

相關文章

ad