闲话少说,直接开始。
Promise.prototype.then()
理论与特点
作用是为 Promise 实例添加状态改变时的回调函数。
我们知道Promise主要处理的异步回调的问题,同时Promise并不直接返回最后的结果,而是将它们放在resolve或者reject里面。那我们怎么可以得到异步回调的值呢,那就要用到then方法。
then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。
then方法里面的两个参数虽然是可选的,但是需要注意如果Promise返回的是reject状态,我们想调用它的值,就必须设置两个参数,在第二个参数上调用reject的值。如果是resolve状态,第二个参数可以不写。
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)- 因此可以采用
链式写法,即then方法后面再调用另一个then方法。这也是Promise解决回调地狱的关键。
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
如果采用箭头函数,上面的代码可以写得更简洁。
getJSON("/post/1.json").then(
post => getJSON(post.commentURL)
).then(
comments => console.log("resolved: ", comments),
err => console.log("rejected: ", err)
);
源码解析
先上源码:
function then(onFulfilled, onReject) {
// 保存前一个promise的this
const self = this;
return new Promise((resolve, reject) => {
// 封装前一个promise成功时执行的函数
let fulfilled = () => {
try {
const result = onFulfilled(self.value); // 承前
return result instanceof Promise ? result.then(resolve, reject) : resolve(result); //启后
} catch (err) {
reject(err)
}
}
// 封装前一个promise失败时执行的函数
let rejected = () => {
try {
const result = onReject(self.reason);
return result instanceof Promise ? result.then(resolve, reject) : reject(result);
} catch (err) {
reject(err)
}
}
switch (self.status) {
case PENDING:
self.onFulfilledCallbacks.push(fulfilled);
self.onRejectedCallbacks.push(rejected);
break;
case FULFILLED:
fulfilled();
break;
case REJECT:
rejected();
break;
}
})
}
下面我们进行分析。从前一节的理论部分我们可以得到什么内容呢?
-
then方法内部包含两个参数,分别是resolved状态的回调函数和reject状态的回调函数。注意这两个参数都是可选的。可选的是什么意思,就是可以都写,可以写一个,也可以不写。但不写又对应什么状态呢?相信你已经猜到了,就是pending。这样一看then方法内部一共有三种情况。分别是pending,resolve和reject。三种情况我们会怎么设置?用if else?肯定用Switch case更加具有语义化。那我们对这三种情况分别进行分析。
- Pending状态:其实pending状态我们无论在Promise或者then方法中都几乎用不到,但是我们还是要考虑。我们知道then方法中,resolve和reject都是可选的。但他们又是怎么可以调用呢?是不是想到Pending到resolve或reject的转换。对,就是在Pending状态下分别将resove和reject的返回值添加到对应的回调函数上。
case PENDING: self.onFulfilledCallbacks.push(fulfilled); self.onRejectedCallbacks.push(rejected); break;- Resolve:直接执行:
case FULFILLED: fulfilled(); break;- Reject:直接执行:
case REJECT: rejected(); break;
then方法返回的是一个新的Promise实例
所以它的代码大致上是:
function then(onFulfilled, onReject) {
// 保存前一个promise的this
const self = this;
return new Promise((resolve, reject) => {
// 封装前一个promise成功时执行的函数
let fulfilled = () => {
...
}
// 封装前一个promise失败时执行的函数
let rejected = () => {
...
}
switch (self.status) {
case PENDING:
self.onFulfilledCallbacks.push(fulfilled);
self.onRejectedCallbacks.push(rejected);
break;
case FULFILLED:
fulfilled();
break;
case REJECT:
rejected();
break;
}
})
}
- 到最后我们我们就只剩下怎么写relove和reject函数了。那怎么写呢?
- 首先肯定要获取Promise的值。
- 第二步对它进行处理
- 最后处理可能报错的问题。
这三步处理下来之后,也就是then方法的实现源码了。
应用
then方法主要的用处链式调用解决回调地狱问题。
Promise.prototype.catch()
Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
getJSON('/posts.json').then(function(posts) {
// ...
}).catch(function(error) {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
上面代码中,getJSON()方法返回一个 Promise 对象,如果该对象状态变为resolved,则会调用then()方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误。另外,then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获。
Promise 对象后面要跟catch()方法,这样可以处理 Promise 内部发生的错误。catch()方法返回的还是一个 Promise 对象,因此后面还可以接着调用then()方法。
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2);
});
};
someAsyncThing()
.catch(function(error) {
console.log('oh no', error);
})
.then(function() {
console.log('carry on');
});
// oh no [ReferenceError: x is not defined]
// carry on
上面代码运行完catch()方法指定的回调函数,会接着运行后面那个then()方法指定的回调函数。如果没有报错,则会跳过catch()方法。
Promise.prototype.finally()
finall方法顾名思义就是最后所执行的代码。
finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});