Promise是異步代碼實現控制流的一種方式。這一方式可讓你的代碼乾淨、可讀而且健壯。node
好比,你用來異步處理文件事件的回調代碼:express
fs.readFile('directory/file-to-read', function(err, file){ if (error){ //handle error } else { //do something with the file } });
你之前可能據說過Node很快會陷入回調地獄,以上就是緣由。做爲一個node開發者你會遇到不少的異步代碼,也就會遇到不少的回調(callback)。json
這些回調仍是比較簡單的。可是你會須要在一個動做完成以後繼續作別的動做,所以回調會不斷的嵌套。你會發現很快這些代碼就很難閱讀了,更別提維護了。好比:promise
fs.readFile('directory/file-to-read', function(err, file){ if (error){ //handle error } else { //do something with the file fs.mkdir('directory/new-directory', function(err, file){ if (error) { //handle error } else { //new directory has been made fs.writeFile('directory/new-directory/message.txt', function(err, file){ if(error) { // handle error } else { // File successfully created } }); } }); } });
在上面的例子中我想要異步的讀取一個文件,而後建立一個目錄並建立一個文件。你能夠看到這個簡單的三步走的任務變成了多麼醜陋的嵌套的代碼,尤爲是一旦你再在這些代碼中添加邏輯控制的時候,代碼更是不可想象醜陋。框架
咱們爲何要在node中使用Promise異步
以上面對的代碼做爲例子,咱們將探究如何用Promise解決上面說到的問題:async
fs.readFileAsync('directory/file-to-read') .then(function(fileData){ return fs.mkdirAsync('directory/new-directory'); }) .then(function(){ return fs.writeFileAsync('directory/new-directory/message.txt'); })
Promise給咱們提供了一個更加清晰和健壯的方式編寫異步代碼。最開始的方法返回一個promise,這個promise上能夠調用‘then’方法。‘then’以後還能夠調用更多的‘then’。每一個‘then’均可以訪問上一個‘then’返回的信息。每個‘then’方法返回的示例均可以再調用一個‘then’。這每每就是另外一個異步的調用。ide
Promise也讓你更加容易的把代碼分解到多個文件中。好比:post
function readFileandMakeDirectory(){ return fs.readFileAsync('directory/file-to-read') .then(function(fileData){ return fs.mkdirAsync('directory/new-directory'); }); } //The following will execute once the file has been read and a new directory has been made readFileandMakeDirectory() .then(function(){ return fs.writeFileAsync('directory/new-directory/message.txt'); })
很容易建立返回promise的方法。當你須要把代碼分解到不一樣的文件中的時候會很是有用。好比,你可能有一個路由讀取一個文件,讀取其內容,而且把文章內容以json的形式返回出來。你能夠把代碼分解成多個返回promise的組件。測試
//routes/index.js var router = require('express').Router(); var getFileExcerpt = require('../utils/getFileExcerpt') router.get('/', function(){ getFileExcerpt.then(function(fileExcerpt){ res.json({message: fileExcerpt}); }); }); module.exports = router; //utils/getFileExcerpt.js var Promise = require('bluebird'); var fs = Promise.promisifyAll(require('fs')); module.exports = function getPost(){ return fs.readFileAsync(file, 'utf8').then(function(content){ return { excerpt: content.substr(0, 100) } }); }
以上代碼也清楚的代表任何一個返回的‘then’後面能夠再調用‘then’。
處理錯誤
使用promise處理錯誤很是簡單。當執行一堆‘then’方法的過程當中出現錯誤的時候Bluebird會找到最近的.catch方法執行。你能夠在‘then’鏈中嵌入catch方法。上例能夠改寫爲:
fs.readFileAsync('directory/file-to-read') .then(function(fileData){ return fs.mkdirAsync('directory/new-directory'); }) .then(function(){ return fs.writeFileAsync('directory/new-directory/message.txt'); }) .catch(function(error){ //do something with the error and handle it });
你可使用catch處理錯誤。
可是我要用的模塊不返回promise
你會注意到上面的例子中使用了‘fs.writeFileAsync'和’fs.mkdirAsync’。若是你檢查node的文檔你會看到這些方法並不存在。FS不會返回promise。
儘管如此,bluebird提供了一個很是有用的功能來promise化不返回promise的模塊。好比,promise化fs模塊,只須要簡單地require bluebird模塊和一個被promise化的fs模塊。
var Promise = require('bluebird'); var fs = Promise.promisifyAll(require('fs'));
建立你本身的Promise
由於你能夠promise化模塊,因此你不會須要寫不少的代碼來建立promise。而後,即便如此知道如何建立也是很必要的。建立promise須要提供resolve和reject的回調方法。每個都須要傳對了:
//myPromise.js var Promise = require('bluebird'); module.exports = function(){ return new Promise(function(resolve, reject){ tradiationCallbackBasedThing(function(error, data){ if (err) { reject(err); } else { resolve(data) } }); }); }
這樣就完成了promise化。接下來,你就可使用這個技術把你想要promise化的都寫成promise的形式。
測試Promise
當我測試服務端代碼的時候,我最喜歡的框架是mocha和chai。須要注意,測試異步代碼的時候你須要告訴mocha異步代碼何時執行完成。不然,他只會繼續執行下面的測試,這時就會出錯。
這個時候,只須要簡單地調用mocha在it部分中提供的回調方法:
it('should do something with some async code', function(done){ readPost(__dirname + '/../fixtures/test-post.txt') .then(function(data){ data.should.equal('some content inside the post'); done(); }) .catch(done); });
Promise很是有用,使用node編寫異步代碼的時候強烈建議你使用promise。
能夠經過閱讀Bluebird的API文檔瞭解更多。