要想解释promise是什么,首先我们要对JavaScript有一定的认识:在JS世界内所有代码都是单线程执行的。这就导致其所有网络操作、浏览器事件,都必须是异步执行。
异步执行可以通过回调函数来实现:
function callBack() {
console.log('执行完成');
}
console.log('执行回调函数前...');
setTimeout(callBack, 2000);
console.log('执行回调函数后...');
结果为:
执行回调函数前...
执行回调函数后...
(2秒钟后)
执行完成
Ajax也是典型的异步操作。
由上可见,异步操作就是在将来某个时间点触发一个函数调用。但像上面这种写法似乎并不简练美观,而且也不利于代码复用。
在ES6里统一规范了Promise对象,来高效地实现异步操作。
利用promise来写一段简单的代码,实现功能:随机生成两个数字a、b,若a>b,则等待一段时间后返回成功,否则返回失败。
function test(resolve, reject) {
var a = Math.random();
var b = Math.random();
console.log('a = ' + a + ' and b = ' + b); // 打印出随机生成的a、b的值
setTimeout(function () {
if (a > b) { // 若a > b则执行resolve
console.log('call resolve...');
resolve('a的确大于b');
} else {
console.log('call reject...');
reject('a小于b了!');
}
}, 2000);
}
new Promise(test).then(function (result) {
console.log('符合要求,' + result);
}).catch(function (reason) {
console.log('不符合要求,' + reason);
});
程序执行后,结果里除去打印的a、b的值,就有两种可能结果:
call resolve...
符合要求,a的确大于b
或者是
call reject...
不符合要求,a小于b了!
据此,我们便知道了Promise的用法是:new Promise(function(resolve, reject))
每个Promise可接受一个参数,该参数为一个函数,该函数又有两个参数resolve和reject,分别为成功之后的回调函数和失败之后的回调函数。
当异步操作执行成功后,会将异步操作结果作为参数传入 resolve 函数并执行,此时 Promise对象状态从 pending 变为 fulfilled;
失败则会将异步操作的错误作为参数传入 reject 函数并执行,此时 Promise对象状态从pending 变为 rejected。
所以说,Promise有两种状态转换:pending -> fulfilled, pending -> rejected,且状态一旦改变就不会再变。
并且Promise有三个原型方法,分别是.then(),.catch()和.finally()
a、.then()方法提供一个或两个回调函数作为参数,第一个是pending -> fulfilled时执行,第二个是pending -> rejected时执行,且第二个参数可选。
Promise.then(function(value){
// success
},function(error){
// failure
})
b、.catch()方法提供一个回调函数作为参数,在pending -> rejected时执行。其作用等同于.then()方法的第二个参数。
Promise.then(undefined, function(error){
//failure
})
// 等价于
Promise.catch(function(error){
//failure
})
c、.finally()方法是无论Promise结局如何都会执行的回调函数。
至此,我们已经知道了Promise的简单用法,那么加以扩展:
1. Promise().then().then()...catch()多任务串行执行
// 定义一个test函数,判断a是否大于b,是则1秒后返回Promise的resolve并将a--与b组成的数组作为result参数,否则返回Promise的reject。
// 由于test要被Promise调用,所以参数设置成了数组(因为Promise的resolve只返回一个参数result)
function test(x) {
var a = x[0];
var b = x[1];
console.log('a = ' + a + ' and b = ' + b);
if (a > b) {
a--;
return new Promise(function (resolve,reject) {
console.log('符合要求,a的确大于b。');
setTimeout(function () {
resolve([a, b]);
}, 1000);
});
} else {
return new Promise(function (resolve,reject) {
console.log('不符合要求,a比b小了!');
setTimeout(function () {
reject('Promise end.');
});
});
}
}
var p = new Promise(function (resolve,reject) {
console.log('start new Promise...');
resolve([4, 1]);
});
p.then(test)
.then(test)
.then(test)
.then(test)
.then(test)
.catch((reason) => { console.log(reason)} );
结果如下:这便是Promise的多任务串行执行。
可以抽象化的理解为用户去办理业务,职员a同意(resolve)后将处理结果交给职员b,职员b同意(resolve)后将处理结果交给职员c...但凡中间有一个人不同意(reject),则直接回绝,不再执行(catch)。
2. Promise.all([p1,p2,...]) 多任务并行执行
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
console.log(results); // 获得一个Array: ['P1', 'P2']
});
都要成功才进入then,返回结果数组。
3. Promise.race([p1,p2,...]) 多任务赛跑
then()和catch(),谁先调用算谁的,其它任务中断。
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
console.log(result); // 'P1'
});
当resolve返回参数是另一个Promise对象
// code1
var p1 = new Promise((resolve, reject)=>resolve('ff'));
var p2 = new Promise((resolve, reject)=>reject(p1));
p2.then(result => console.log('1', result)).catch(error => console.log('2', error))
// code2
var p1 = new Promise((resolve, reject)=>reject('ff'));
var p2 = new Promise((resolve, reject)=>resolve(p1));
p2.then(result => console.log('1', result)).catch(error => console.log('2', error))
code1返回结果是这是为什么呢?原来,当用普通对象resolve一个Promise时,该Promise对象就立刻将普通对象作为result参数进入resolve状态,但是如果用thenable(即带有then方法的对象,可以简单理解为是Promise)来resolve另一个Promise对象时,如本例code2中用p1来resolve另一个p2,则p2的状态自动与p1进行绑定,此时,p1是何种状态,则p2进入何种状态。如本例code2中p1进入了reject,则p2也进入reject。