Promise
Promise是异步编程的⼀种解决⽅案。
Promise对象有以下两个特点:
- 对象的状态不受外界影响。
Promise对象代表一个异步操作,有3种状态:Pending、Fulfilled和Rejected。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。 - ⼀旦状态改变就不会再变,任何时候都可以得到这个结果。
Promise对象的状态改变只有两种可能:从Pending变为Fulfilled和从Pending变为Rejected。只要这两种情况发⽣,状态就凝固了,不会再 变,⽽是⼀直保持这个结果,这时就称为Resolved(已定型)。
基本用法
// 创建一个promise对象
var promise = new Promise(function(resolve, reject) {
...
if(/*异步操作成功*/){
resolve(data);
}else {
reject(err)
}
)
Promise构造函数接受⼀个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不⽤⾃⼰部署。
- resolve函数的作⽤是:Pending-->Resolved,成功时调⽤,并将异步操作的结果作为参数传递出去;
- reject函数的作⽤是,Pending-->Rejected,失败时调⽤,并将异步操作报出的错误作为参数传递出去。
Promise实例⽣成以后,可以⽤then⽅法分别指定Resolved状态和Rejected状态的回调函数。
promise.then(function(value) {
// success
}, function(err){
// failure
})
then ⽅法可以接受两个回调函数作为参数。成功回调和失败回调,其中,第⼆个函数是可选的,不⼀定要提供。这两个函数都接受Promise对象传出的值作为参数。
需要注意的是:
- Promise新建后会立即执行,
- then方法指定的回调函数将在一步操作后执行
手写Promise:
从Promise的使用来看可以分为两部分:新建实例和调用实例的then方法。 所以我们也可以针对这两部分分别实现:
- 实现Promise构造函数
- 实现then方法
实现Promise构造函数
首先从构造函数出发,根据上文对Promise的描述,我们来实现这个Promise构造函数。
Promise构造函数接受⼀个函数作为参数,该函数的两个参数分别是resolve和reject。他们的作用是将state从Pending变为fulfilled或者rejected
function Promise (executor) {
this.state = 'pending';
this.data = undefined;
this.reason= undefined;
this.fn1Callbacks=[];
this.fn2Callbacks=[];
let resolve = value => {
if(this.state === 'pending'){
this.state = 'fulfilled';
this.data = value;
for(let i = 0; i<this.fn1Callbacks.length; i++){
self.fn1Callbacks[i](value);
}
}
};
let reject = reason => {
if(this.state === 'pending'){
this.state = 'rejected';
this.reason = reason;
for (let i = 0; i < self.fn2Callback.length; i++) {
self.fn2Callback[i](reason);
}
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
实现 then 方法
当Promise的状态发生了改变,不论是成功或是失败都会调用then方法。
Promise实例具有then方法,即then方法是定义在原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数并在异步完成时执行这个函数。
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
基于上面的讨论我们很容易得出结论:
- then方法可以在实例上调用。因此then 方法的实现是在Promise的 prototype上。
- 他的作用是为Promise实例添加状态改变时的回调函数并在异步完成时执行这个函数
- then方法会返回一个Promise,而且是返回一个新的Promise(详情)对象。
下面我们来看看promise实现
// then方法接收两个参数,fn1,fn2,分别为Promise成功或失败后的回调
Promise.prototype.then = function(fn1, fn2) {
var self = this
var promise2
// 首先对入参 fn1, fn2做判断
fn1 = typeof fn1 === 'function' ? fn1 : function(v) {}
fn2 = typeof fn2 === 'function' ? fn2 : function(r) {}
if (self.status === 'resolved') {
return promise2 = new Promise(function(resolve, reject) {
//todo
})
}
if (self.status === 'rejected') {
return promise2 = new Promise(function(resolve, reject) {
//todo
})
}
if (self.status === 'pending') {
return promise2 = new Promise(function(resolve, reject) {
// todo
})
}
}
首先是对输入的成功回调和失败回调进行类型判断,明确是个函数再执行它,接下来是处理Promise中可能存在的有三种可能的状态,我们分三个if块来处理,在里面分别都返回一个new Promise。
所以,接下来的逻辑是:
- 如果 promise 状态是 resolved,需要执行 fn1 ;
- 如果 promise 状态是 rejected, 需要执行fn2 ;
- 如果 promise 状态是 pending, 我们并不能确定调用 fn1 还是 fn2 ,只能先把方法都保存在 fn1Callback, fn2Callback 数组中。等到Promise的状态确定后再处理。
根据上面的逻辑,填充下面代码:
Promise.prototype.then = function(fn1, fn2) {
var self = this
var promise2
fn1 = typeof fn1 === 'function' ? fn1 : function(v) {}
fn2 = typeof fn2 === 'function' ? fn2 : function(r) {}
if (self.status === 'resolved') {
return promise2 = new Promise(function(resolve, reject) {
// 把 fn1、fn2 放在 try catch 里面,毕竟 fn1、fn2 是用户传入的,报错嘛,很常见
try {
var x = fn1(self.data)
// fn1 执行后,会有返回值,通过 resolve 注入到 then 返回的 promise 中
resolve(x)
} catch (e) {
reject(e)
}
})
}
if (self.status === 'rejected') {
return promise2 = new Promise(function(resolve, reject) {
try {
var x = fn2(self.data)
reject(x)
} catch (e) {
reject(e)
}
})
}
if (self.status === 'pending') {
return promise2 = new Promise(function(resolve, reject) {
this.fn1Callback.push(function(value){
try {
var x = fn1(self.data);
resolve(x)
} catch (e) {
reject(e)
}
})
this.fn2Callback.push(function(value) {
try {
var x = fn2(self.data);
reject(x)
} catch (e) {
reject(e)
}
})
})
}
}