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