ECMAscript 6 原生提供了 Promise 对象。MDN上关于Promise是这么解释的:
Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。
一个Promise
通常有以下几种状态
待定pending状态的 Promise
对象要么会通过一个值被兑现(fulfilled),要么会通过一个原因(错误)被拒绝(rejected)。
对于一个Promise
,常用的做法是将 Promise.then()
,Promise.catch()
和 Promise.finally()
这些方法将进一步的操作与一个变为已敲定状态的 Promise
关联起来。
以以下代码举例:
const p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('operate in promise');
// resolve将Promise状态标记为fulfilled,此后执行Promise.then中的回调函数,其中参数1可以被then中的回调函数接收
resolve(1);
// reject方法将Promise状态标记为rejected
// reject(new Error(someError))
}, 1000);
});
p.then((result) => {
// Promise状态变为rejected后,执行异常处理回调函数
// 其中参数result为上面resolve方法的参数:1
console.log('promise fulfilled, return value: ', result);
}).catch((err) => {
// Promise状态变为rejected后,执行异常处理回调函数
// 其中err为reject方法的参数
console.log('promise rejected', err);
}).finally(() => {
// 成功或异常处理完成后,执行finally中的回调函数
console.log('finally.')
});
上面的例子中,执行结果如下:
通常的做法是上例中的形式,即p.then(() => {}).catch(() => {}).finally(() => {})
,不过查阅MDN文档发现,链式调用除了上面的形式,还有这样的形式,即将成功处理和失败处理同放于then的回调函数中,如下:
p.then((result) => {
// handle fulfilled
}, (err) => {
// handle rejected
})
这种形式在写法上和上面的形式有点区别,但是作用是一样的,就不展开描述。
多个Promise并发执行可以借助Promise.all()
方法执行,该方法返回一个 Promise 实例。方法定义如下:
Promise.all<T>(values: Iterable<T | PromiseLike<T>>): Promise<T[]>;
是待多个Promise全部返回成功状态时,才将Promise状态标记为成功fullfilled,只要有任何一个Promise
返回失败状态,状态则被标记为失败rejected,在可以在MDN查看Promise.all()
方法的解释:Promise.all()
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(11);
resolve(11);
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(22);
resolve(22);
}, 1000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(33);
resolve(44);
}, 1000)
})
Promise.all([p1, p2, p3]).then(results => {
// p1,p2,p3中全部成功后执行
console.log('all resolved: ', results)
}).catch((err) => {
// p1,p2,p3中只要有一个失败,执行catch方法
console.log('promise rejected', err);
});
对于多个Promise
实例对象p1、p2、p3,如果需要串行执行,即先执行p1,成功后执行p2,再成功后执行p3,主要借助于Promise.then()
。实例代码:
const p1 = (preResult) => {
// preResult为前一个Promise返回值,由于p1之前没有Promise,此处为undefined
return new Promise((resolve, reject) => {
console.log('pre return value: ', preResult);
resolve(11)
});
}
const p2 = (preResult) => {
// preResult为前一个Promise返回值,此处即p1的返回值11
return new Promise((resolve, reject) => {
console.log('pre return value: ', preResult);
resolve(22)
});
}
const p3 = (preResult) => {
// preResult为前一个Promise返回值,此处即p2的返回值22
return new Promise((resolve, reject) => {
console.log('pre return value: ', preResult);
resolve(33)
});
}
p1().then(p2).then(p3).then((result) => {
// result为链式调用中最后一个Promise的返回值,此处为p3返回值33
console.log('last return value: ', result);
})
对于上面两个例子中,Promise
数量都是固定的,调用起来非常方便,当然对于Promise.all
方式调用,可以调用任意个数量的Promise
,只要传进来的参数是个数组就可以。但是对于串行调用,即p1().then(p2).then(p3).then((result) => {})
的形式,如果Promise
数量不是固定的,就无法通过枚举的形式一个一个列出来加到then
中进行调用。
此时我们可以借助于Array.reduce()
进行串行调用,Array.reduce()
方法通常用于数组的累加求和,方法定义如下:
interface Array<T> {
/**
* Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
* @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
* @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
*/
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
}
其中第一个参数callbackfn
为回调函数,第二个参数initialValue
非必须,为初始值。Array.reduce
可以处理数组中的每一个元素,并将处理后的结果作为参数传递给下一个处理函数。这样,我们可以在callbackfn
中构造一个Promise,并传递给下一个处理函数。
const arr = [];
const p1 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(new Date());
resolve();
}, 1000)
})
}
arr.push(p1);
const p2 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(new Date());
resolve();
// resolve();
}, 1000)
})
}
arr.push(p2);
const p3 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(new Date());
resolve();
}, 1000)
})
}
arr.push(p3);
// 可以在数组arr中添加其他Promise...
arr.reduce((prev, curv) => {
return prev.then(() => {
return curv().then()
})
}, Promise.resolve()).then(() => {
console.log('all resolved');
}).catch(err => {
console.log('error');
console.error(err);
});
执行结果如下:
可以看到上面的例子中,多个Promise依次串行执行,每个Promise
在上个Promise
执行完成后再执行。
当然实际上Promise
串行执行的引用远远不止这么简单,在此不展开来讲解。