Bluebird-NodeJs的Promise

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文檔瞭解更多。

相關文章
相關標籤/搜索