先来看看promise的几种用法:- Promise.prototype.then:then方法有两个参数,第一个参数是Promise成功时执行的callback,第二个是Promise失败时执行的callback,then方法返回的是一个新的Promise实例
new Promise((resolve, reject) => resolve()).then(() => { console.log('success'); }, () => { console.log('err') })复制代码- Promise.prototype.catch:用于指定发生错误时的回调函数
如果没有指定reject函数,最后就会执行catch函数new Promise((resolve, reject) => { reject('err');}).then().catch(e => { console.log(e); // => Error: err})复制代码- Promise.resolve:定义在Promise类本身的方法,可以通过Promise.resolve()调用,相当于直接将状态改为成功态
Promise.resolve('hahaha').then(data => { console.log(data); // => 'hahaha'})复制代码- Promise.reject:定义在Promise类本身的方法,可以通过Promise.reject()调用,相当于直接将状态改为失败
Promise.reject('hahaha').then(data => { console.log(data); // => 'hahaha'})复制代码- Promise.prototype.all:将多个Promise执行的结果放到一个数组中返回
let promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('promise1'); }, 1500);})let promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve('promise2'); }, 2000);})Promise.all([promise1, promise2]).then(data => { console.log(data); // => ["promise1", "promise2"]})复制代码 下面我们自己来实现一个符合 PromiseA+规范 的Promise我们先来简单的用一下promise:
console.log(1)new Promise(() => { console.log(2)});console.log(3)复制代码因为我们都知道promise是异步的,所以按理会依次输出 1,3,2 然后我们运行之后发现 它依次输出的是 1,2,3 当我们使用then的时候:
console.log(1)Promise.resolve().then(() => { console.log(2);})console.log(3)复制代码再次运行,发现结果和我们之前想的是一样的,会依次输出 1,3,2 这是因为promise的callback是立即执行的,只有then方法是异步的
A promise must be in one of three states: pending, fulfilled, or rejected.
- promise必须有三个状态 pending(等待态) fulfilled(成功态) rejected(失败态)
- 当 state 是 pending 的时候
- 可以将当前状态改变成其他状态(fulfilled or rejected)
- 当 state 是 fulfilled 的时候
- 不能转换成其他状态
- 必须有一个value(成功之后的值)
- 当 state 是 rejected 的时候
- 不能转换成其他状态
- 必须有一个reason(失败的原因)
class Promise { constructor(executor) { // 每一个promise实例都有自己的三个状态,我们用一个变量来保存当前的状态 this.status = 'pending'; // 用来保存执行成功时的值 this.value; // 用来保存执行失败时的原因 this.reason; // 用来将当前状态改变为成功态的方法 let resolve = val => { // 只有当前状态是`pending` 才可以改变状态和值 if (this.status === 'pending') { this.value = val; this.status = 'fulfilled'; } } // 用来将当前状态改变为失败态的方法 let reject = reason => { // 只有当前状态是`pending` 才可以改变状态和值 if (this.status === 'pending') { this.reason = reason; this.status = 'rejected'; } } // 执行executor可能会直接抛出异常,我们用try catch包起来 try { executor(resolve, reject); } catch (e) { // 如果抛出异常,我们直接将状态改为失败态 reject(e); } }}复制代码一个promise必须提供一个then方法去获取当前的或最终的value or reason
promise的then方法有两个参数,分别是成功时执行的方法onFulfilled和失败时执行的方法onRejected
- onFulfilled 和 onRejected都是可选的参数
- 如果onFulfilled是一个function
- 它必须在当前promise的状态变成fulfilled之后被调用,将promise的value(成功之后的值)作为他的第一个参数
- 在状态是fulfilled之前不能调用它
- 只能被调用一次
- 如果onRejected是一个function
- 它必须在当前promise的状态变成rejected之后被调用,将promise的reason(失败的原因)作为他的第一个参数
- 在状态是rejected之前不能调用它
- 只能被调用一次
promise.then(onFulfilled, onRejected)复制代码class Promise { constructor(executor) {...} then(onFulfilled, onRejected) { // 如果当前状态是成功态,我们就执行成功的回调,并将存起来的成功的值value传过去 if (this.status === 'fulfilled') { onFulfilled(this.value); } // 如果当前状态是失败态,我们就执行失败的回调,并将存起来的失败原因reason传过去 if (this.status === 'rejected') { onRejected(this.reason); } }}复制代码好了,我们现在已经可以简单的测试一下了
new Promise((resolve, reject) => { resolve('完美');}).then(data => { console.log(data); // => 完美})复制代码完美,但是那么问题来了,如果我们的 resolve 或 reject 是异步的呢?
new Promise((resolve, reject) => { setTimeout(() => { resolve('完美'); }, 1000);}).then(data => { console.log(data); }, err => { console.log(err);})复制代码运行之后我们发现什么都没有输出,onFulfilled 和 onRejected 都没有打印东西,那说明他们都没有执行,let me think think...
这是因为如果我们的 resolve 或 reject是异步的,当我们的then执行的时候,状态还没有改变,还是pending状态,所以当然什么都不会执行。我们可以先把 onFulfilled 和 onRejected 存起来,等状态改变的时候依次执行对应的callback。A: 这难道是?B: 没错,这就是订阅发布模式。下面我们来改写一下:
class Promise { constructor(executor) { this.status = 'pending'; this.value; this.reason; // 用来保存成功时执行的回调函数 this.onSuccessCallback = []; // 用来保存失败时执行的回调函数 this.onErrorCallback = []; let resolve = val => { if (this.status === 'pending') { this.value = val; this.status = 'fulfilled'; // 状态改变时 依次执行成功的回调 this.onSuccessCallback.forEach(fn => fn()); } } let reject = reason => { if (this.status === 'pending') { this.reason = reason; this.status = 'rejected'; // 状态改变时 依次执行失败的回调 this.onErrorCallback.forEach(fn => fn()); } } // 执行executor可能会直接抛出异常,我们用try catch包起来 try { executor(resolve, reject); } catch (e) { // 如果抛出异常,我们直接将状态改为失败态 reject(e); } } then(onFulfilled, onRejected) { if (this.status === 'fulfilled') { onFulfilled(this.value); } if (this.status === 'rejected') { onRejected(this.reason); } if (this.status === 'pending') { // 将成功回调和失败回调都存起来,等待状态改变,再依次执行对应的方法 this.onSuccessCallback.push(() => { onFulfilled(this.value); }); this.onErrorCallback.push(() => { onRejected(this.reason); }); } }}复制代码- onFulfilled 和 onRejected都是异步调用(微任务)
- onFulfilled 和 onRejected必须作为一个函数被执行
- then方法必须返回一个promise
promise2 = promise1.then(onFulfilled, onRejected)复制代码
- 如果onFulFilled or onRejected返回了一个值x,运行promise解析程序
- 如果onFulfilled or onRejected抛出了一个异常e,promise2的状态必须是rejected,并将e作为onRejected的参数
- 如果onFulfilled不是一个function并且promise1是fulfilled,promise2必须也是fulfilled并且使用和promise1相同的value
- 如果onRejected不是一个function并且promise1是rejected,promise2必须也是rejected并且使用和promise1相同的reason
我们用promise的时候经常promise.then().then().then() ...这种写法叫链式调用,那怎么样才能继续调用then方法呢,规范规定then方法必须返回一个promise实例,这样就可以实现链式调用了
class Promise { contructor() {...} then(onFulfilled, onRejected) { // 如果onFulfilled和onFulfilled 不是一个函数,我们给一个默认值 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val; onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }; // 我们将所有东西都包到一个promise实例中,最后返回这个实例,这样就可以实现链式调用 let promise2; // `onFulfilled` 和 `onRejected`都是异步调用,我们先用一个定时器实现异步调用 promise2 = new Promise((resolve, reject) => { if (this.status === 'fulfilled') { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); } if (this.status === 'rejected') { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); } if (this.status === 'pending') { // 将成功回调和失败回调都存起来,等待状态改变,再依次执行对应的方法 this.onSuccessCallback.push(() => { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); }); this.onErrorCallback.push(() => { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); }); } }); return promise2; }}复制代码这里可能就会有疑惑了,如果有一个返回值x就运行promise解析程序resolvePromise,这是什么鬼?
我们先来看看规范:
promise的解析程序是将promise2和x作为参数的函数
- 如果promise2和x是一个promise,那么抛出一个TypeError
- 如果x是一个object或function
- 使用变量then存储x.then
- 如果x.then会抛出异常e,调用promise的reject,并将e作为它的参数
- 如果then是一个function,使用call把它的this指向x,它的第一个参数是resolvePromise,第二个参数是rejectPromise:
- 当resolvePromise被y值调用的时候,继续执行解析程序
- 当rejectPromise执行的时候,调用promise的reject并将将失败原因r作为它的参数
- 如果then不是一个object or function,调用promise的resolve并将x作为它的参数
- 如果x不是一个object or function,调用promise的resolve并将x作为它的参数
总结下来就两点:
- 如果promise2和x相等,就抛出一个TypeError,我们先来看一下
let p = new Promise((resolve, reject) => { // 返回当前promise实例 return p;});p.then(data => { console.log(data);}, err => { console.log(err);});复制代码运行上面代码,我们会发现promise抛出了一个异常,他告诉我们TypeError: Chaining cycle detected for promise,这是因为p的成功还是失败取决于自己,自己再等待自己的执行结果,所以他既不会成功也不会失败
- 如果onFulFilled or onRejected返回了一个值x,运行promise解析程序resolvePromise
返回值x有可能是一个常量,对象,也有可能是一个promise,这个程序的作用就是如果 x 是一个promise,那就将 x 一直解析到常量位置
let p = new Promise((resolve, reject) => { resolve(new Promise((resolve, reject) => { resolve(1111); }))})复制代码let resolvePromise = (promise2, x, resolve, reject) => { // 如果promise2和x相等,就抛出一个类型错误 if (promise2 === x) { return reject(new TypeError('错了')); } // 只允许调用一次resolvePromise let called; // 如果x不是一个常量继续解析 if (x !== null && (typeof x === 'function' || typeof x === 'object')) { // 调用x.then的时候可能会报错,因为我们有可能和别人的Promise库混用 try { let then = x.then; // 如果then是一个函数,证明x是一个promise,继续解析 if (typeof then === 'function') { then.call(x, y => { if (called) { return; } else { called = true; } resolvePromise(promise2, y, resolve, reject); }, r => { if (called) { return; } else { called = true; } reject(r); }) } else { // 说明x可能是一个普通对象,不是一个promise resolve(x); } } catch (e) { if (called) { return; } else { called = true; } reject(e); } } else { // 说明x是一个常量,直接执行resolve resolve(x); }}复制代码接下来我们来实现Promise.resolve Promise.reject Promise.all
class Promise { contructor {...} then() {...} // 其实Promise.reject和Promise.reject非常简单 static resolve(value) { return new Promise((resolve) => { resolve(value); }) } static reject(reason) { return new Promise((resolve, reject) => { reject(reason); }) } // all方法 static all(promises) { return new Promise((resolve, reject) => { // 用来保存结果 let arr = []; let index = 0; const saveData = (i, data) => { arr = data; if (++index === promises.length) { resolve(arr); } } for (let i = 0; i < promises.length; i++) { promises.then(data => { saveData(i, data); }, reject) } }) }}复制代码好了,接下来我们来一个完整版的promise
class Promise { contructor() { this.status = 'pending'; this.value; this.reason; // 用来保存成功时执行的回调函数 this.onSuccessCallback = []; // 用来保存失败时执行的回调函数 this.onErrorCallback = []; let resolve = val => { if (this.status === 'pending') { this.value = val; this.status = 'fulfilled'; // 状态改变时 依次执行成功的回调 this.onSuccessCallback.forEach(fn => fn()); } } let reject = reason => { if (this.status === 'pending') { this.reason = reason; this.status = 'rejected'; // 状态改变时 依次执行失败的回调 this.onErrorCallback.forEach(fn => fn()); } } // 执行executor可能会直接抛出异常,我们用try catch包起来 try { executor(resolve, reject); } catch (e) { // 如果抛出异常,我们直接将状态改为失败态 reject(e); } } static resolve(value) { return new Promise((resolve) => { resolve(value); }) } static reject(reason) { return new Promise((resolve, reject) => { reject(reason); }) } static all(promises) { return new Promise((resolve, reject) => { // 用来保存结果 let arr = []; let index = 0; const saveData = (i, data) => { arr = data; if (++index === promises.length) { resolve(arr); } } for (let i = 0; i < promises.length; i++) { promises.then(data => { saveData(i, data); }, reject) } }) } then(onFulfilled, onRejected) { // 如果onFulfilled和onFulfilled 不是一个函数,我们给一个默认值 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val; onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }; // 我们将所有东西都包到一个promise实例中,最后返回这个实例,这样就可以实现链式调用 let promise2; // `onFulfilled` 和 `onRejected`都是异步调用,我们先用一个定时器实现异步调用 promise2 = new Promise((resolve, reject) => { if (this.status === 'fulfilled') { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); } if (this.status === 'rejected') { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); } if (this.status === 'pending') { // 将成功回调和失败回调都存起来,等待状态改变,再依次执行对应的方法 this.onSuccessCallback.push(() => { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); }); this.onErrorCallback.push(() => { setTimeout(() => { try { // 有一个返回值x,运行解析函数resolvePromise let x = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }, 0); }); } }); return promise2; }}let resolvePromise = (promise2, x, resolve, reject) => { // 如果promise2和x相等,就抛出一个类型错误 if (promise2 === x) { return reject(new TypeError('错了')); } // 只允许调用一次resolvePromise let called; // 如果x不是一个常量继续解析 if (x !== null && (typeof x === 'function' || typeof x === 'object')) { // 调用x.then的时候可能会报错,因为我们有可能和别人的Promise库混用 try { let then = x.then; // 如果then是一个函数,证明x是一个promise,继续解析 if (typeof then === 'function') { then.call(x, y => { if (called) { return; } else { called = true; } resolvePromise(promise2, y, resolve, reject); }, r => { if (called) { return; } else { called = true; } reject(r); }) } else { // 说明x可能是一个普通对象,不是一个promise resolve(x); } } catch (e) { if (called) { return; } else { called = true; } reject(e); } } else { // 说明x是一个常量,直接执行resolve resolve(x); }}复制代码
作者:asdff
链接:https://juejin.im/post/5b58300b6fb9a04fea58a27e
|
|