Promise让我们通过链式调用的方式来解决回调过多的问题,特别是在异步代码中,可以保证我们代码的整洁性和可读性。以Promise A+的规范来从0到1实现promise可以你对它的认知变的很深刻.
Promise-承诺
Promise 代表了一个异步操作的最终结果, Promise的主要交互方式是通过它的then方法, then方法注册回调用于接收Priomse的结果,不管这个结果是最终的值,还是无法实现Promise的原因。
Promise的状态
Promise一共有三种状态
- pending:初始的状态,在resolve前和reject前处于的状态
- fulfilled:promise被resolve后处于的状态,这个状态不能够再改变,且拥有一个不可变的值(value)
- rejected:promise被reject后处于的状态,这个状态不能够再改变,且拥有一个不可变的值(value)
graph TD
pending --> fulfilled
pending --> rejected
所以说promise一旦状态发生转移就不可再转移为其他状态,也就是此过程是不可逆的
const PENDING = 'pending',
FULFILLED = 'fulfilled',
REJECTED = 'rejected';
class MyPromise {
constructor (exector) {
// promise的初始状态是pending
this.status = PENDING;
}
}
A+规范术语
- promise: 是拥有一个then方法的对象或函数.
- thenable: 是一个定义了 then 方法的对象或函数。这个主要是用来兼容一些老的Promise实现,只要一个Promise实现是thenable,也就是拥有then方法的,就可以跟Promises/A+兼容.
- value: 是resolve出来的值,也就是promise成功时的值,可以是任意一个JS中合法的值包括(undefied, thenable和promise等)
- exception: 异常,在promise里面用throw抛出来的值
- reason: 是reject出来的值,也就是promise拒绝的值reason,表示拒绝的原因
resolve和reject
- resolve将处于pending状态的promise修改为fulfilled状态
- reject将处于pending状态的promise修改为rejected状态
- 并且在构造函数中执行传进来的exector函数
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
}
// 如果有错误(用户在exector中遇到的错误都要reject出去)
try {
exector(resolve, reject);
} catch(e) {
reject(e);
}
then
在使用promise的时候如果操作成功就会调用then里面的onFulFilled,如果失败则调用onRejected.所以对应代码中需要检查promise的status,根据stauts来决定走onFulfilled和onRejected
//then 中的两个函数可缺省
onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : (val) => val;
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => reason;
if (this.status === FULFILLED) {
onFulFilled(this.value);
}
if (this.status === REJECTED) {
onRejected(this.reason);
}
如果我们像这样使用promise,上面的代码就无法处理异步的resolve
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('asyns resolve')
}, 1000);
});
p.then(res => {
console.log(res, 'FULFILLED')
})
当实例化的传参exector函数还没执行完我们就调用了then函数,这时候status的状态还是pending,这时候我们肯定是不能够调用onFulfilled和onRejected,只有在exector函数中主动resolve和reject的时候我们才知道是成功还是失败,所以我们应该在pending的时候把onFulfilled和onRejected利用好数组存起来等exector有了结论再通过resolve和reject来把数组都执行一遍.
class MyPromise {
constructor (exector) {
// promise的初始状态是pending
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulFilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onFulFilledCallbacks.forEach(fn => fn());
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
exector(resolve, reject);
} catch(e) {
reject(e);
}
}
then (onFulFilled, onRejected) {
//then 中的两个函数可缺省
onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : (val) => val;
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => reason;
if (this.status === FULFILLED) {
onFulFilled(this.value);
}
if (this.status === REJECTED) {
onRejected(this.reason);
}
//如果exector函数还没有resolve和reject
if (this.status === PENDING) {
this.onFulFilledCallbacks.push(() => onFulFilled(this.value));
this.onRejectedCallbacks.push(() => onRejected(this.reason));
}
}
}
上面这种先收集回调,等条件满足的时候再拿出来运行的模式即发布订阅,在then中pending状态下push即注册事件,resolve或者reject中执行所有的事件发布。到现在已经完成了异步调用但是还没有完成链式调用.
链式调用
- 规范中then必须返回一个promise
- 如果onFullfilled函数返回的是该promise本身,那么会抛出类型错误
- 如果onFullfilled函数返回的是一个不同的promise,那么执行该promise的then函数,在then函数里将这个promise的状态转移给新的promise III)如果返回的是一个嵌套类型的promsie,那么需要递归。 在规范中还有一条:onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用。这一条的意思是实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。所以在我们执行onFulfilled 和 onRejected的时候都应该包到setTimeout里面去。
then (onFulFilled, onRejected) {
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
try {
setTimeout(() => {
let x = onFulFilled(this.value);
resolvePromise(promise2, x, resolve, reject);
})
} catch (e) {
reject(e);
}
}
if (this.status === REJECTED) {
try {
setTimeout(() => {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
})
} catch (e) {
reject(e);
}
}
if (this.status === PENDING) {
this.onFulFilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulFilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
});
}
});
return promise2;
}
还有一个最重要的就是在then里面判断onFulfilled和onRejected返回的是否是一个函数或对象或一个promise,对这些场景进行resolve或者reject.
function resolvePromise(promise2, x, resolve, reject) {
// 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
// 这是为了防止死循环
if (promise2 === x) {
throw new TypeError('chaining cycle');
}
// 如果 x 为对象或者函数
if ((x && typeof x === 'object') || typeof x ==='function') {
let used = false;
try {
let then = x.then;
// 如果then为函数
if (typeof then === 'function') {
then.apply(x, [
(y) => {
// 防止多次调用
if (used) return;
used = true;
// y可能还是个promise,所以递归继续解析直到返回一个普通值
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
if (used) return;
used = true;
reject(r);
}
])
} else {
// 如果 then 不是函数,以 x 为参数执行 promise
if (used) return;
used = true;
resolve(x);
}
} catch(e) {
if (used) return;
used = true;
reject(e);
}
} else {
// 返回一个普通值
resolve(x);
}
}
可以使用promises-aplus-tests的测试用例来测试,使用示例测试通过...