为什么使用promise
回调表达式程序异步和管理并发的两个缺陷,缺乏顺序性和可信性,如下:
1>nodejs异步读取文件,异步函数:
var fs = require('fs');
function readJSON(filename,callback){
fs.readFile(filename,'utf8',function(err,res){
if(err){
return callback(err,null);
}
try{
var res = JSON.parse(res);
}catch(ex){
callback(ex)
}
callback(null,res);
});
}
异步文件必须包含两个参数,文件名和回调函数,上诉调用函数时候不知道callback的参数,还有就是多个嵌套
2>异步函数调用
fs.readFile('file1.txt','utf8',function(err,res){
fs.readFile('file2.txt','utf8',function(err,res){
fs.readFile('file2.txt','utf8',function(err,res){
console.log(res);
});
});
});
通过第三方提供给我们任务结束的能力,而不是等待第三方提供数据,最后使用自己的代码决定下一步干啥
2.定义
promise代表必须异步处理的函数返回的值或者异常,promise将异步对象与回调函数脱离,then异步操作上绑定回调函数
1》三种状态:pending,fulfilled,rejected
2》promise唯一的接口then,支持链式调用
function sendXHR(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.open('get', 'QueryUser', true);
xhr.onload = function(){
if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304){
resolve(xhr.responseText);
}else{
reject(new Error(xhr.statusText));
}
};
xhr.onerror = function(){
reject(new Error(xhr.statusText));
}
xhr.send(null)
}
3.实现
1>实现异步对象状态与回调函数剥离,还能使用回调函数正常执行
2>链式调用与管理状态的实现
//使用then方法添加回调函数,把这次回调函数return的结果当做return的promise的resolve的参数
Promise.prototype.then = function(onResolved, onRejected){
var self = this;
return new Promise(function(resolve, reject){
var onResolvedFade = function(val){
var ret = onResolved?onResolved(val):val;//这一步主要是then方法中传入的成功回调函数通过return来进行链式传递结果参数
if(Promise.isPromise(ret)){//回调函数返回值也是promise的时候
ret.then(function(val){
resolve(val);
});
}
else{
resolve(ret);
}
};
var onRejectedFade = function(val){
var ret = onRejected?onRejected(val):val;
reject(ret);
};
self.handlers.push(onResolvedFade);
if(self._status === FULFILLED){
onResolvedFade(self._value);
}
if(self._status === REJECTED){
onRejectedFade(self._value);
}
});
}
onResolved, onRejected默认为函数
onFulfilled: 在promise已完成后调用且仅调用一次该方法,该方法接受promise最终值作参数
onRejected: 在promise被拒绝后调用且仅调用一次该方法,该方法接受promise拒绝原因作参数;两个函数都是异步事件的回调,符合JavaScript事件循环处理流程
该方法必须返回一个promise:varpromise2=promise1.then(onFulfilled,onRejected);
通过上面的代码可以看出,前面提出的2个问题得到了解决:
1.在promise对象中有3个属性,state,value,handlers,这3个属性解决了状态和回调的脱离,并且在调用then方法的时候才将回调函数push到handlers属性上面(此时state就是1,可以在后面的代码中执行onResolve)
2.链式调用通过在then方法中返回的promise对象实现,并且通过onResolvedFade将上一个回调的返回值当做这次的result参数来执行进行传递。
4.resolution决议过程
一个promise和一个值[[result]](promise,x),其中x为promise,promise就直接使用x的状态,若x为对象或者函数,获取x.then的引用,若抛出异常则是拒绝promise,否则会引用赋值给then,若then为函数,就调用传递两个回调函数参数(resolvePromise,rejectPromise)
5.es6中的promise javascript支持Promise,浏览器chrome也支持该功能)
var promise = new Promise((resolve, reject) => {
setTimeout(function() {
resolve('完成');
}, 10);
});
promise.then((msg) => {
console.log('first messaeg: ' + msg);
})
promise.then((msg) => {
console.log('second messaeg: ' + msg);
});
输出如下:
5-1. promise模式:构造器 newPromise(function(resolve,reject){});
实例化时,会初始一个异步任务,在异步任务完成或失败时,调用resolve或reject函数来完成或拒绝返回的Promise对象。另外需要注意的是,若传入的函数执行抛出异常,那么这个promsie将被拒绝
5-2. promise模式:静态方法 Promise.all([...iterable])
all方法接受一个或多个promsie(以数组方式传递),返回一个新promise,该promise状态取决于传入的参数中的所有promsie的状态:
var p1 = new Promise((resolve, reject) => {
setTimeout(function(){
console.log('p1决议');
resolve('p1');
}, 10);
});
var p2 = new Promise((resolve, reject) => {
setTimeout(function(){
console.log('p2决议');
resolve('p2');
}, 10);
});
Promise.all( [p1, p2] )
.then((msgs) => {
// p1和p2完成并传入最终值
console.log(JSON.stringify(msgs));
})
.then((msg) => {
console.log( msg );
});
5-3. promise模式:Promise.race(iterable)
race方法返回一个promise,只要传入的诸多promise中的某一个完成或被拒绝,则该promise同样完成或被拒绝,最终值或拒绝原因也与之相同。
Promise.resolve(x)resolve方法返回一个已决议的Promsie对象:
- 若x是一个promise或thenable对象,则返回的promise对象状态同x;
- 若x不是对象或函数,则返回的promise对象以该值为完成最终值;
- 否则,详细过程依然按前文Promsies/A+规范中提到的规则进行。
该方法遵循Promise/A+决议规范。
5-4. promise模式:Promsie.reject(reason)返回一个使用传入的原因拒绝的Promise对象。
5-5.举实例
对比回调函数与promise实现x和y相加,等到俩个值完全执行好后,执行相加运算
1>回调函数实现
function add(getx,gety,cb){
var x,y;
getx({function(xVal){
x=xval;
if(y!=undefined)cb(x+y)
}})
gety({function(xVal){
y=yval;
if(x!=undefined)cb(x+y)
}})
}
add(fetchx,fetchy,function(sum){console.log(sum)})
2>利用promise实现
function add(xp,yp){
return Promise.all([xp,yp]); //调用并创建一个promise来等待xp,yp决议
.then(function(values){return values[0]+values[1]}); //等待.all的promise
}
add(fetchx(),fetchy()) //直接调用返回值(promise),可能未就绪
.then(function(sum){console.log(sum)},function(err){console.log(err)});