promise 一个标准,Promise也已经纳入了ES6,而且高版本的chrome、firefox浏览器都已经原生实现了Promise,它描述了异步调用的返回结果,包括正确返回结果和错误处理,可以解耦异步调用中的复杂嵌套问题。关于详细的说明文档可以参考 Promises/A+ 。目前实现 promise 标准的模块有很多,如 Q 、 bluebird 和 Deferred ,下面我们以 Q 为例,介绍一下 promise 在 nodejs 中的使用方法。
有这么一个场景:有三个文件file1、file2、file3,按照先后顺序依次读取,然后拼接起来(或者说一个函数用到另外一个函数的值)。
传统通过回调嵌套方式实现同步,来保障拼接的字符串不会乱。
var str = 'begin';
fs.readFile('file1.txt', function(err, data1) {
str += data1;
fs.readFile('file2.txt', function(err, data2) {
str += data2;
fs.readFile('file3.txt', function(err, data3) {
str += data3;
});
});
});
我们现在采用基于promise规范的Q
安装:npm install q
引入项目:var Q = require('q');
首先将上面3层嵌套代码解耦和,并用Q.deferred构建
var Q = require('q'),
fs = require('fs');
var str = 'begin';
function read1(data) {
var deferred = Q.defer();
fs.readFile('file1.txt', function(err, data1) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(data += '~' + data1);
}
});
return deferred.promise;
}
function read2(data) {
var deferred = Q.defer();
fs.readFile('file2.txt', function(err, data2) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(data += '~' + data2);
}
});
return deferred.promise;
}
function read3(data) {
var deferred = Q.defer();
fs.readFile('file3.txt', function(err, data3) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(data += '~' + data3);
}
});
return deferred.promise;
}
链式用q的then串联,链式实现了函数值依次传递。
read1('begin').then(read2).then(read3).done(function(data) {}, function(err) {});
Q.all可以传入数组,数组含有n个函数,数组内的函数是异步执行的,spread收集每个函数的返回值
Q.all([
read1(),
read2(),
read3()
]).spread(function(data1, data2, data3) {
console.log(data1, data2, data3);
});
或者
Q.spread([
read1(),
read2(),
read3()
]).spread(function(data1, data2, data3) {
console.log(data1, data2, data3);
});
函数数组方式,当函数中出现err(deferred.reject)或者没有返回deferred.resolve,done、spread不会返回值。Q.allSettled是用于保障函数出现异常时候,不会中断其它函数运行,最后统一返回值。
我们测试一下,让read1只返回deferred.reject
function read1(data) {
var deferred = Q.defer();
fs.readFile('file1.txt', function(err, data1) {
// if (err) {
deferred.reject(err);
// } else {
// deferred.resolve(data += '~' + data1);
// }
});
return deferred.promise;
}
执行
Q.allSettled([
read1(),
read2(),
read3()
]).spread(function(data1, data2, data3) {
console.log(data1, data2, data3);
});
或者
Q.allSettled([
read1(),
read2(),
read3()
]).then(function (results) {
results.forEach(function (result) {
if (result.state === "fulfilled") {
var value = result.value;
} else {
var reason = result.reason;
}
});
});
结果
{ state: 'rejected', reason: null } { state: 'fulfilled', value: 'undefined~love
' } { state: 'fulfilled', value: 'undefined~you' }
state: 'rejected' 标识运行出现错误;state:'fulfilled' 完成;reason:null 打印err内容;value:'xxx' 返回值
如果你有一组函数并且需要顺序执行,可以通过手动的then方式
read1.then(read2).then(read3));
var funcs = [read1, read2, read3];
var result = Q('begin');//定义一个初始值begin
funcs.forEach(function(f) {
result = result.then(f);//遍历传参
});
var funcs = [read1, read2, read3];
funcs.reduce(function(soFar, f) {
return soFar.then(f);
}, Q('begin'));
使用fail函数
Q.allSettled([
read1(),
read2(),
read3()
]).fail(function(error) {
});
Promise有个then方法,then方法可以接受3个函数作为参数。前两个函数对应Promise的两种状态的回调函数fulfiled 和 rejected,第三个函数用于处理进度信息。
uploadFile()
.then(function () {
// Success uploading the file
}, function (err) {
// There was an error, and we get the reason for error
}, function (progress) {
// We get notified of the upload's progress as it is executed
});
uploadFile().progress(function (progress) {
// We get notified of the upload's progress
});
结束部分引用了:http://blog.csdn.net/boyzhoulin/article/details/40592837的内容,如下
通常,对于一个promise链,有两种结束的方式。第一种方式是返回最后一个promise
如 return foo().then(bar);
第二种方式就是通过done来结束promise链
如 foo().then(bar).done()
为什么需要通过done来结束一个promise链呢? 如果在我们的链中有错误没有被处理,那么在一个正确结束的promise链中,这个没被处理的错误会通过异常抛出。
var Q = require('q');
/**
*@private
*/
function getPromise(msg, timeout, opt) {
var defer = Q.defer();
setTimeout(function() {
console.log(msg);
if (opt)
defer.reject(msg);
else
defer.resolve(msg);
}, timeout);
return defer.promise;
}
/**
*没有用done()结束的promise链
*由于getPromse('2',2000,'opt')返回rejected, getPromise('3',1000)就没有执行
*然后这个异常并没有任何提醒,是一个潜在的bug
*/
getPromise('1', 3000)
.then(function() {
return getPromise('2', 2000, 'opt')
})
.then(function() {
return getPromise('3', 1000)
});
/**
*用done()结束的promise链
*有异常抛出
*/
getPromise('1', 3000)
.then(function() {
return getPromise('2', 2000, 'opt')
})
.then(function() {
return getPromise('3', 1000)
})
.done();