ES6 异步解决方案 Promise
背景
JS 经常会遇到一些异步任务,所谓异步任务,就是需要经过一段时间 或 当某个时机到达后才能得到结果的任务
例如:
- 使用 ajax 请求服务器,当服务器完成响应后拿到响应结果
- 监听按钮是否被点击,当按钮被点击后拿到某个文本框的值
- 使用 setTimeout 等待一段时间,当时间到达后做某些事情
面对这样的场景,JS 没有一种标准的模式来进行处理,我们处理这些问题的方式是杂乱的,这就导致了不同的人书写的异步任务代码使用方式不一致。
另外,过去常常使用回调的方式来处理异步场景,这种方式又容易产生回调地狱(callback hell)
ES6 总结了各种异步场景,并提取出一种通用的异步模型
ES6 的异步处理模型
ES6 将异步场景分为两个阶段和三种状态
两个阶段:unsettled(未决) 和 settled(已决)
三种状态:pending(挂起)、resolved(完成)、rejected(失败)
他们的关系图如下:
当任务处于未决阶段时,它一定是 pending 挂起状态,表示任务从开始到拿到结果之间的过程。比如:网络完成了各种配置,也发送了请求,但是请求结果还没有拿到。
当任务处于 已决阶段时,它只能是 resolved 和 rejected两种状态的一种,表示任务有了一个结果。比如:从服务器拿到了数据(resolved)、网络不好没有拿到数据(rejected)
任务开始时,始终是未决阶段,那任务如何才能走向已决阶段呢?
ES6 认为,任务在未决阶段的时候,有能力将其推向已决。比如,当从服务器拿到数据后,我们就从未决阶段推向已决的 resolved 状态,如果网络不好,导致出错了,我们就从未决阶段推向已决的 rejected 状态
我们把从未决推向已决的 resolved 状态的过程,叫做 resolve,从未决推向已决的 rejected 状态的过程,叫做 reject,如下图所示
这种状态和阶段的变化是不可逆的,也就是说,一旦推向了已决,就无法重新改变状态
任务从未决到已决时,可能附带一些数据,比如:跑步完成后的用时、网络请求后从服务器拿到的数据
任务已决后(有了结果),可能需要进一步做后续处理,如果任务成功了(resolved),有后续处理,如果任务失败了(rejected),仍然可能有后续处理
我们把针对 resolved 的后续处理,称之为 thenable,针对 rejected 的后续处理,称之为 catchable
Promise 的基本使用
ES 官方制定了一个全新的 API 来适配上面提到的异步模型,这个 API 即 Promise
Promise 是一个构造函数,通过new Promise()
可以创建一个任务对象,构造函数的参数是一个函数,用于处理未决阶段的事务,该函数的执行是立即同步执行的。在函数中,可以通过两个参数自主的在合适的时候将任务推向已决阶段
var pro = new Promise((resolve, reject) => {
//未决阶段的代码,这些代码将立即执行
//...
//在合适的时候,将任务推向已决
//resolve(数据):将任务推向resovled状态,并附加一些数据
//reject(数据):将任务推向rejected状态,并附加一些数据
})
注意
- 任务一旦进入已决后,所有企图改变任务状态的代码都将失效
- 以下代码可以让任务到达 rejected 状态
- 调用 reject
- 代码执行报错
- 抛出错误
拿到 Promise 对象后,可以通过 then 方法指定后续处理
pro.then(thenable, catchable)
//或
pro.then(thenable)
pro.catch(catchable)
无论是 thenable 还是 catchable,均是下面格式的函数
function (data){
//data为状态数据
}
注意:后续处理函数一定是异步函数,并且放到微队列中