Promise
Promise对象用于表示一个异步操作的最终完成(或失败)及其结果值
描述
一个 Promise 对象代表一个在这个 promise 被创建出来时不一定已知的值。它让您能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。 这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者。
一个 Promise 必然处于以下几种状态之一:
- 待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled): 意味着操作成功完成。
- 已拒绝(rejected): 意味着操作失败。
待定状态的 Promise 对象要么会通过一个值被兑现(fulfilled),要么会通过一个原因(错误)被拒绝(rejected)。当这些情况之一发生时,我们用 promise 的 then 方法排列起来的相关处理程序就会被调用。如果 promise 在一个相应的处理程序被绑定时就已经被兑现或被拒绝了,那么这个处理程序就会被调用,因此在完成异步操作和绑定处理方法之间不会存在竞争状态。
因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回的是 promise, 所以它们可以被链式调用。
不要和惰性求值混淆: 有一些语言中有惰性求值和延时计算的特性,它们也被称为“promises”,例如 Scheme。JavaScript 中的 promise 代表的是已经正在发生的进程, 而且可以通过回调函数实现链式调用。 如果您想对一个表达式进行惰性求值,就考虑一下使用无参数的"箭头函数": f = () =>表达式 来创建惰性求值的表达式,使用 f() 求值。
为什么使用Promise
本质上 Promise 是一个函数返回的对象,我们可以在它上面绑定回调函数,这样我们就不需要在一开始把回调函数作为参数传入这个函数了。
假设现在有一个名为 functionWithoutPromise() 的函数,它接收一些配置和两个回调函数,然后异步执行任务。一个回调函数在任务成功执行时被调用,另一个则在出现异常时被调用。
以下为使用 functionWithoutPromise() 的示例:
function functionWithoutPromise(taste,successCallback,failedCallback){
let result = 1
if (result == 1){
successCallback()
}else {
failedCallback()
}
}
functionWithoutPromise('taste', successCallback, failureCallback)
这里我们通过Promise改写
function functionReturnPromise(taste){
return new Promise((resolve, reject)=>{
resolve(taste)
})
}
functionReturnPromise('taste').then(successCallback,failureCallback)
我们把这个称谓异步函数调用,这种形式有若干优点,下面我们将会逐一讨论。
约定
不同于“老式”的传入回调,在使用 Promise 时,会有以下约定:
- 在本轮 事件循环 运行完成之前,回调函数是不会被调用的。
- 即使异步操作已经完成(成功或失败),在这之后通过 then() 添加的回调函数也会被调用。
- 通过多次调用 then() 可以添加多个回调函数,它们会按照插入顺序进行执行。
Promise 很棒的一点就是链式调用(chaining)。 由于 then 和 Promise.prototype.catch() 方法都会返回 promise,它们可以被链式调用——这同时也是一种被称为复合( composition) 的操作。
创建Promise
const myFirstPromise = new Promise((resolve, reject) => {
});
Promise的构造函数接收一个参数,是函数,并且传入两个参数,resolve和reject,分别表示异步执行成功后的回调函数和异步执行失败后的回调函数。
const promise1 = new Promise((resolve, reject) => {
resolve('Success!');
});
promise1.then((value) => {
console.log(value);
});
注意:如果忽略针对某个状态的回调函数参数,或者提供非函数 (nonfunction) 参数,那么 then 方法将会丢失关于该状态的回调函数信息,但是并不会产生错误。如果调用 then 的 Promise 的状态(fulfillment 或 rejection)发生改变,但是 then 中并没有关于这种状态的回调函数,那么 then 将创建一个没有经过回调函数处理的新 Promise 对象,这个新 Promise 只是简单地接受调用这个 then 的原 Promise 的终态作为它的终态。
结合上例,我们可以通俗理解,promise1的resolve并没有提供一个fulfillment状态的回调函数,仅仅是提供了一个变量,那个在then方法处理时,简单的将该字符串变量“Success!”作为参数传递下去。
链式调用
then() 函数会返回一个和原来不同的新的 Promise:
const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);
或者:
const promise2 = doSomething().then(successCallback, failureCallback);
promise2 不仅表示 doSomething() 函数的完成,也代表了你传入的 successCallback 或者 failureCallback 的完成,这两个函数也可以返回一个 Promise 对象,从而形成另一个异步操作,这样的话,在 promise2 上新增的回调函数会排在这个 Promise 对象的后面。
基本上,每一个 Promise 都代表了链中另一个异步过程的完成。
在过去,要想做多重的异步操作,会导致经典的回调地狱:
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
现在,我们可以把回调绑定到返回的 Promise 上,形成一个 Promise 链:
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
注意,catch()和then()一样,会形成一个新的Promise,所以,如果catch()之后的其他then()会继续执行:
new Promise((resolve, reject) => {
console.log('初始化');
resolve();
})
.then(() => {
throw new Error('某个错误');
console.log('执行「这个」');
})
.catch(() => {
console.log('执行「那个」');
})
.then(() => {
console.log('执行「最后一个」,无论前面发生了什么');
});
输出:
初始化
执行“那个”
执行“最后一个”,无论前面发生了什么
因为抛出了某个错误,所以“执行「这个」”不会输出,catch()之后的then()继续执行。
同样的,错误也会顺着Promise链向下传递。
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log(`Got the final result: ${finalResult}`))
.catch(failureCallback);
也就是说,如果在第1行出现异常,第2,3行就不会执行,异常会随着promise链向下传播直到被catch()捕获。和try……catch……类似:
try {
let result = syncDoSomething();
let newResult = syncDoSomethingElse(result);
let finalResult = syncDoThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
|