基本用法Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// success
}).catch(err){
// failure
};
概述
Promsie有三种状态,分别是函数执行时的pending,成功时的resolved(fulfilled)以及失败时的rejected。
Promise接受一个回调函数作为参数,并立即执行之。回调参数接收系统传进的两个函数作为参数:resolve和reject,分别用来改变promise的状态为成功及失败。
当我们根据条件调用resolve函数时,由下面promise.then()(的第一个参数回调)接受之,当我们根据条件调用reject函数或抛出错误时,由下面的promise.catch()(或promise.then的第二个参数回调)接收之。可以向这两个函数中传递参数以供then和catch的回调函数接收使用。如果没有resolve函数,then里的内容永远也不会执行。
这样,就可以保证then/catch方法内的回调总在传给Promise构造器的回调函数执行后执行。
Promise对象特点
- 对象的状态不受外界影响。
- 一旦状态改变,就不会再变
Promise对象缺点:
- 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
- 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
- 当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
ajax
下面是一个通过Promise实现ajax的例子
var getJSON = function(url) {
var promise = new Promise(function(resolve, reject){
var client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
function handler() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
实现serise
传统方法
我们使用jquery进行ajax请求,最后需要两个ajax请求所获得的数据,传统方法大概会这么做:
$.get('/test', null, function (data) {
$.get('/test2', null, function (data2) {
console.log(data+':'+data2);
})
});
Promise解决方案
Promise提供了更好的解决方案
var p1 = new Promise(function (resolve, reject) {
$.get('/test', null, function (data) {
console.log(data);
resolve(data);
});
});
var p2 = new Promise(function (resolve, reject) {
$.get('/test2', null, function (data2) {
console.log(data2);
resolve(data2);
})
});
Promise.all([p1, p2]).then(function (arr) {
console.log(arr[0]+':'+arr[1])
});
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。只有这2个实例的状态都变成fulfilled,或者其中有一个变为rejected,才会调用Promise.all方法后面的回调函数。
于此相对,Promise还提供了静态的race方法,只要有一个实例完成,就执行then里的方法。
实现waterfall
传统方法
我们使用jquery进行ajax请求,当第二个回调函数需要第一个回调函数的返回值作为原始数据时,原始的js代码大概会这么做:
$.get('/test', null, function (data) {
$.get('/test2', null, function (data2) {
if(data === 'ok'){
console.log("It's okay " + data2)
}else{
console.log('not okay')
}
})
});
两个回调函数还能应付的过来,如果有三个,四个,甚至五个,就难免觉得吃力了。Promise提供了解决方案:
第一种方案
var pro = new Promise(function (resolve, reject) {
console.log('pro执行了');
// 使pro2等待pro更明显
setTimeout(function () {
$.get('/test', null, simpleHandler);
},5000);
function simpleHandler(data) {
resolve(data);
}
});
var pro2 = new Promise(function (resolve, reject) {
console.log('pro2执行了');
$.get('/test2', null, simpleHandler);
function simpleHandler(data) {
console.log('pro2的simpleHandler调用了');
pro.then(function (data1) {
console.log('pro返回了!');
if (data1 === 'ok') {
resolve(data);
} else {
reject(data)
}
})
}
});
pro2.then(function (data) {
console.log("It's okay " + data);
});
依次输出:
console.log('pro执行了');
console.log('pro2执行了');
console.log('pro2的simpleHandler调用了');
等待5s
console.log('pro返回了!');
console.log("It's okay John Doe");
可见,当一个Promise对象的回调里引入另一个Promise对象时,会等待引入的Promise对象执行完毕。
第二种解决方案
var getData = function () {
return new Promise(function (resolve, reject) {
console.log('pro执行了');
// 使pro2等待pro更明显
setTimeout(function () {
$.get('/test', null, simpleHandler);
},5000);
function simpleHandler(data) {
resolve(data);
}
});
};
var getData2 = function (data1) {
return new Promise(function (resolve, reject) {
console.log('pro2执行了');
$.get('/test2', null, simpleHandler);
function simpleHandler(data) {
console.log('pro2的simpleHandler调用了');
if (data1 === 'ok') {
resolve(data);
} else {
reject(data)
}
}
});
};
getData().then(function (data1) {
return getData2(data1);
}).then(function (data) {
console.log("It's okay " + data);
});
采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。格式为:
pro1.then(()=>pro2).then(()=>pro3).then(... ...);
括展方法
done
如果promise方法链中的最后一个回调函数发生错误,由于promise的机制,我们无从知道,所以可以为之括展done方法,处于回调链的尾端。
asyncFunc().then(f1) .catch(r1) .then(f2).done();
Promise.prototype.done = function (onFulfilled, onRejected) {
this.then(onFulfilled, onRejected)
.catch(function (reason) {
});
};
finally
无论发生错误与否最后都会执行的方法
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => {callback(value)},
reason => {callback(reason)}
);
};