Bluebird-NodeJs的Promise


Promise是異步代碼實現控制流的一種方式。這一方式可以讓你的代碼干凈、可讀並且健壯。

比如,你用來異步處理文件事件的回調代碼:

fs.readFile('directory/file-to-read', function(err, file){
    if (error){
        //handle error
    } else {
        //do something with the file
    }
});

你以前可能聽說過Node很快會陷入回調地獄,以上就是原因。作為一個node開發者你會遇到很多的異步代碼,也就會遇到很多的回調(callback)。

這些回調還是比較簡單的。但是你會需要在一個動作完成之后繼續做別的動作,因此回調會不斷的嵌套。你會發現很快這些代碼就很難閱讀了,更別提維護了。比如:

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解決上面說到的問題:

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’。這往往就是另一個異步的調用。

Promise也讓你更加容易的把代碼分解到多個文件中。比如:

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM