开发中Promise
是及其罕用的语法,基本上对于异步的解决大都是通过Promise
来进行实现。Promise标准有很多,ES6最终采纳的是Promise/A+ 标准
,所以以下代码也根本是基于这个标准来进行编写的。
首先咱们先列举Promise的所有实例办法跟静态方法
实例办法
- then:
new Promise((resolve, reject) => {...}).then(() => {console.log('rsolve胜利回调')}, () => {console.log('reject失败回调')})
- catch:
new Promise((resolve, reject) => {...}).catch(() => {console.log('reject失败办法')})
- finally:
new Promise((resolve, reject) => {...}).finally(() => {console.log('成功失败都进入')})
- 以上办法调用都将返回新的
Promise
静态方法
- resolve:
Promise.resolve(value)
返回Promise
实例 - reject:
Promise.reject(value)
返回Promise
实例 - all:
Promise.all(promises)
: 传入数组格局的Promise
并返回新的Promise
实例,胜利便依照程序把值返回进去,其中一个失败则间接变成失败 - race:
Promise.race(promises)
: 传入数组格局的Promise
并返回新的Promise
实例,胜利与失败取决第一个的实现形式
Promise
状态一旦确定变不可再发生变化,有以下三个状态:pending
、fulfilled
、rejected
Promise
在浏览器中的实现是放于微工作队列中的,须要做微工作的解决(JavaScript中的Event Loop(事件循环)机制
)
1.申明Promise的实例办法
class Promise { _value _state = 'pending' _queue = [] constructor(fn) { if (typeof fn !== 'function') { throw 'Promise resolver undefined is not a function' } /* new Promise((resolve, reject) => { resolve: 胜利 reject: 失败 }) */ fn(this._resolve.bind(this), this._reject.bind(this)) } // 接管1-2参数,第一个为胜利的回调,第二个为失败的回调 then(onFulfilled, onRejected) { // 有可能曾经resolve了,因为Promise能够提前resolve,而后then办法前面注册 if (this._state === 'fulfilled') { onFulfilled?.(this._value) return } // reject同理 if (this._state === 'rejected') { onRejected?.(this._value) return } // Promise还没有实现,push到一个队列,到时候实现的时候,执行这个队列外面对应的函数 this._queue.push({ onFulfilled, onRejected, }) } // 接管失败的回调 catch(onRejected) { // 相当于间接调用then传入失败的回调 this.then(null, onRejected) } // 胜利与失败都执行的回调 finally(onDone) { const fn = () => onDone() this.then(fn, fn) } // 胜利resolve _resolve(value) { // 状态确定了,就不再发生变化了 if (this._state !== 'pending') return this._state = 'fulfilled' // 把值存起来,当再次调用的时候间接取这个值就行,因为Promise一旦确定就不会产生扭转了 this._value = value // 执行后面.then办法外面push函数模式的参数,这样就执行对应的办法了。 this._queue.forEach((callback) => { callback.onFulfilled?.(this._value) }) } // 失败reject _reject(error) { // 状态确定了,就不再发生变化了 if (this._state !== 'pending') return this._state = 'rejected' this._value = error this._queue.forEach((callback) => { callback.onRejected?.(this._value) }) }}
调用逻辑:
- 通过
then
办法传入函数模式的参数,也就是onFulfilled
=>then((onFulfilled, onRejected) => {...})
- 在
then
办法中把onFulfilled
函数放入_queue
这个汇合中。 =>this._queue.push({ onFulfilled, onRejected })
- 等异步回调实现,执行
resolve
函数,这个时候就调用_queue
收集好的通过then
办法注册的函数。对立执行这些函数,这样就达到异步回调实现,执行对应的then
办法外面的函数
// 后果打印const p = new Promise((resolve, reject) => { setTimeout(() => { resolve('success') }, 1000)})p.then((res) => { console.log(res) // => success})// rejectconst p1 = new Promise((resolve, reject) => { setTimeout(() => { reject('fail') }, 1000)})p1.catch((res) => { console.log(res) // => fail})// finallyconst p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve() }, 1000)})p2.finally(() => { console.log('done') // => done})
在线代码演示
2. 微工作解决以及返回Promise
a. 进行微工作解决
在浏览器中 Promise
实现之后会被推入微工作,所以咱们也须要进行这块的解决。浏览器中应用MutationObserver,node能够应用process.nextTick
class Promise { ... // 推入微工作 _nextTick(fn) { if (typeof MutationObserver !== 'undefined') { // 浏览器通过MutationObserver实现微工作的成果 // 这块能够独自拿进去共用,防止不必要的开销,不然每次都须要生成节点。 const observer = new MutationObserver(fn) let count = 1 const textNode = document.createTextNode(String(count)) observer.observe(textNode, { characterData: true }) textNode.data = String(++count) } else if (typeof process.nextTick !== 'undefined') { // node端通过process.nextTick来实现 process.nextTick(fn) } else { setTimeout(fn, 0) } } // 胜利resolve _resolve(value) { // 状态确定了,就不再发生变化了 if (this._state !== 'pending') return // 推入微工作 this._nextTick(() => { this._state = 'fulfilled' this._value = value this._queue.forEach((callback) => { callback.onFulfilled?.(this._value) }) }) } // 失败reject _reject(error) { // 状态确定了,就不再发生变化了 if (this._state !== 'pending') return // 推入微工作 this._nextTick(() => { this._state = 'rejected' this._value = error this._queue.forEach((callback) => { callback.onRejected?.(this._value) }) }) } ...}
成果演示
b. 返回Promise进行链式调用
通常Promise
会解决多个异步申请,有时候申请之间是有相互依赖关系的。
例如:
const getUser = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ userId: '123' }) }, 500) })}const getDataByUser = (userId) => { return new Promise((resolve, reject) => { setTimeout(() => { // .... resolve({a: 1}) }, 500) })}// 应用getUser().then((user) => { return getDataByUser(user.userId)}).then((res) => { console.log(res)// {a: 1}})
getDataByUser
依赖getUser
申请回来的用户信息,这里就须要用到Promise
链式的调用,上面咱们来改变咱们的代码
class Promise { constructor(fn) { fn(this._resolve.bind(this), this._reject.bind(this)) } ... // 1. 这时候then办法须要返回新的Promise了,因为须要进行链式调用,并且下一个then办法承受上一个then办法的值 // 2. 返回的Promise必定是一个新的Promise,不然就会共用状态跟返回后果了。 // 3. 把上一个then办法中的返回值当做下一个Promise resolve的值 then(onFulfilled, onRejected) { // 返回新的Promise return new Promise((resolve, reject) => { // 有可能曾经resolve了,因为Promise能够提前resolve,而后then办法前面注册,这个时候能够间接把值返给函数就好了 if (this._state === 'fulfilled' && onFulfilled) { this._nextTick(onFulfilled.bind(this, this._value)) return } if (this._state === 'rejected' && onRejected) { this._nextTick(onRejected.bind(this, this._value)) return } /* 把以后Promise的then办法的参数跟新的Promise的resolve, reject存到一起,以此来做关联。 这样就能把上一个Promise中的onFulfilled与新的Promise中的resolve两个关联到一块,而后便能够做赋值之类的操作了。reject同理 */ this._queue.push({ onFulfilled, onRejected, resolve, reject }) }) } // reject同理 _resolve(value) { // 状态确定了,就不再发生变化了 if (this._state !== 'pending') return // 下面示例外面其实返回的是一个Promise,而不是间接返回的值,所以,这里咱们须要做一个非凡解决。 // 就是resolve()的值如果是Promise的对象,咱们须要解析Promise的后果,而后在把值传给resolve if (typeof value === 'object' && typeof value.then === 'function') { // 咱们能够把以后_resolve办法传递上来,因为then办法中的参数,一经下个Promise resolve,便会执行then办法对应的参数,而后把对应的值传入。 // 这样就能取到Promise中的值 // this._resove => obj.onFulfilled?.(this._value) // this._reject => obj.onRejected?.(this._value) value.then(this._resolve.bind(this), this._reject.bind(this)) return } // 推入微工作 this._nextTick(() => { this._state = 'fulfilled' this._value = value this._queue.forEach((obj) => { // 承受onFulfilled返回值 const val = obj.onFulfilled?.(this._value) // reoslve这个值,此时 onFulfilled 是以后Promise then办法中的第一个参数: Promise.then((res) => {consolle.log(res)}) // obj.resolve是新的Promise的resolve函数,这样就把then办法中的返回值传给下一个Promise obj.resolve(val) }) }) } ...}
成果演示
调用逻辑:
- 微工作采纳
MutationObserver
跟process.nextTick
来进行实现 Promise
链式调用,这里通过把then
办法中的(onFulfilled, onRejected)
参数与新返回的Promise
中的(resolve, reject)
关联到一起。- 一旦上一个
Promise
胜利,调用onFulfilled
函数,就能够把onFulfilled
中返回的值,放到新的Promise的resolve中。 - 如果遇到
resolve
的值是Promise
对象,递归进行解析,而后再把值返回进来
残缺代码
class Promise { _value _state = 'pending' _queue = [] constructor(fn) { if (typeof fn !== 'function') { throw new Error('Promise resolver undefined is not a function') } /* new Promise((resolve, reject) => { resolve: 胜利 reject: 失败 }) */ fn(this._resolve.bind(this), this._reject.bind(this)) } // 接管1-2参数,第一个为胜利的回调,第二个为失败的回调 then(onFulfilled, onRejected) { // 返回新的Promise return new Promise((resolve, reject) => { // 有可能曾经resolve了,因为Promise能够提前resolve,而后then办法前面注册,这个时候能够间接把值返给函数就好了 if (this._state === 'fulfilled' && onFulfilled) { this._nextTick(onFulfilled.bind(this, this._value)) return } if (this._state === 'rejected' && onRejected) { this._nextTick(onRejected.bind(this, this._value)) return } // 把以后Promise的then办法的参数跟新的Promise的resolve, reject存到一起,以此来做关联 this._queue.push({ onFulfilled, onRejected, resolve, reject }) }) } // 接管失败的回调 catch(onRejected) { return this.then(null, onRejected) } // 胜利与失败都执行的回调 finally(onDone) { return this.then((value) => { onDone() return value }, (value) => { // console.log(value) onDone() throw value }) } // 推入微工作 _nextTick(fn) { if (typeof MutationObserver !== 'undefined') { // 浏览器 // 这块能够独自拿进去共用,防止不必要的开销,不然每次都须要生成节点。 const observer = new MutationObserver(fn) let count = 1 const textNode = document.createTextNode(String(count)) observer.observe(textNode, { characterData: true }) textNode.data = String(++count) } else if (typeof process.nextTick !== 'undefined') { // node process.nextTick(fn) } else { setTimeout(fn, 0) } } // 胜利resolve _resolve(value) { // 状态确定了,就不再发生变化了 if (this._state !== 'pending') return // 下面示例外面其实返回的是一个Promise,而不是间接返回的值,所以,这里咱们须要做一个非凡解决。 // 就是如果resolve()的如果是Promise的对象,咱们须要解析Promise的后果,而后在把值传给resolve if (typeof value === 'object' && typeof value.then === 'function') { // 咱们能够把以后_resolve办法传递上来,因为then办法中的参数,一经下个Promise resolve,便会执行then办法对应的参数,而后把对应的值传入。 // 这样就能取到Promise中的值 // this._resove => obj.onFulfilled?.(this._value) // this._reject => obj.onRejected?.(this._value) value.then(this._resolve.bind(this), this._reject.bind(this)) return } // 推入微工作 this._nextTick(() => { this._state = 'fulfilled' this._value = value this._queue.forEach((obj) => { // 应用try catch 来捕捉onFulfilled存在函数外部谬误的状况 try { // 承受onFulfilled返回值,如果不存在,把this._value往下传递 const val = obj.onFulfilled ? obj.onFulfilled(this._value) : this._value // reoslve这个值,此时 onFulfilled 是以后Promise then办法中的第一个参数: Promise.then((res) => {consolle.log(res)}) // obj.resolve是新的Promise的resolve函数,这样就把then办法中的返回值传给下一个Promise obj.resolve(val) } catch (e) { obj.reject(e) } }) }) } // 失败reject _reject(error) { if (this._state !== 'pending') return this._nextTick(() => { this._state = 'rejected' this._value = error this._queue.forEach((obj) => { try { const val = obj.onRejected ? obj.onRejected(this._value) : this._value // 以后 reject执行结束之后,会返回新的Promise,应该是能失常resolve的,所以这里要用 resolve, 不应该持续应用reject来让下个Promise执行失败流程 obj.resolve(val) } catch (e) { obj.reject(e) } }) }) }}
申明Promise的静态方法
总共有4个静态方法: Promise.resolve
、Promise.reject
、Promise.all
、Promise.race
,对立返回的都是新的Promise。
class Promise { ... /** * 间接resolve */ static resolve(value) { // 是Promise间接返回 if (value instanceof Promise) { return value } else if (typeof value === 'object' && typeof value.then === 'function') { // 传入的对象含有then办法 const then = value.then return new Promise((resolve) => { then.call(value, resolve) }) } else { // 失常返回值,间接返回新的Promise在resolve这个值 return new Promise((resolve) => resolve(value)) } } /** * 间接reject, 测试下Promise.reject并没做非凡解决,所以间接返回即可。 */ static reject(value) { return new Promise((resolve, reject) => reject(value)) } /** * 传入数组格局的`Promise`并返回新的`Promise`实例,胜利便依照程序把值返回进去,其中一个失败则间接变成失败 */ static all(promises) { return new Promise((resolve, reject) => { let count = 0 let arr = [] // 依照对应的下标push到数组外面 promises.forEach((promise, index) => { // 转换成Promise对象 Promise.resolve(promise).then((res) => { count++ arr[index] = res if (count === promises.length) { resolve(arr) } }, err => reject(err)) }) }) } /** * 传入数组格局的`Promise`并返回新的`Promise`实例,胜利与失败取决第一个的实现形式 */ static race(promises) { return new Promise((resolve, reject) => { promises.forEach((promise, index) => { // 转换成Promise对象 Promise.resolve(promise).then((res) => { // 谁先执行间接resolve, 或reject resolve(res) }, err => reject(err)) }) }) } ...}
Promise实现残缺代码
class Promise { _value _state = 'pending' _queue = [] constructor(fn) { if (typeof fn !== 'function') { throw new Error('Promise resolver undefined is not a function') } /* new Promise((resolve, reject) => { resolve: 胜利 reject: 失败 }) */ fn(this._resolve.bind(this), this._reject.bind(this)) } /** * 接管1-2参数,第一个为胜利的回调,第二个为失败的回调 * * @param {*} onFulfilled * @param {*} onRejected * @return {*} * @memberof Promise */ then(onFulfilled, onRejected) { // 返回新的Promise return new Promise((resolve, reject) => { // 有可能曾经resolve了,因为Promise能够提前resolve,而后then办法前面注册,这个时候能够间接把值返给函数就好了 if (this._state === 'fulfilled' && onFulfilled) { this._nextTick(onFulfilled.bind(this, this._value)) return } if (this._state === 'rejected' && onRejected) { this._nextTick(onRejected.bind(this, this._value)) return } // 把以后Promise的then办法的参数跟新的Promise的resolve, reject存到一起,以此来做关联 this._queue.push({ onFulfilled, onRejected, resolve, reject }) }) } /** * 接管失败的回调 * * @param {*} onRejected * @return {*} * @memberof Promise */ catch(onRejected) { return this.then(null, onRejected) } /** * 胜利与失败都执行的回调 * * @param {*} onDone * @return {*} * @memberof Promise */ finally(onDone) { return this.then((value) => { onDone() return value }, (value) => { onDone() // 间接报错,能够在try catch中捕捉谬误 throw value }) } /** * 间接resolve * * @static * @param {*} value * @return {*} * @memberof Promise */ static resolve(value) { if (value instanceof Promise) { return value } else if (typeof value === 'object' && typeof value.then === 'function') { // 传入的对象含有then办法 const then = value.then return new Promise((resolve) => { then.call(value, resolve) }) } else { return new Promise((resolve) => resolve(value)) } } /** * 间接reject, 测试下reject在Promise.reject中没做非凡解决 * * @static * @param {*} value * @return {*} * @memberof Promise */ static reject(value) { return new Promise((resolve, reject) => reject(value)) } /** * 传入数组格局的`Promise`并返回新的`Promise`实例,胜利便依照程序把值返回进去,其中一个失败则间接变成失败 * * @static * @param {*} promises * @memberof Promise */ static all(promises) { return new Promise((resolve, reject) => { let count = 0 let arr = [] if (Array.isArray(promises)) { if (promises.length === 0) { return resolve(promises) } promises.forEach((promise, index) => { // 转换成Promise对象 Promise.resolve(promise).then((res) => { count++ arr[index] = res if (count === promises.length) { resolve(arr) } }, err => reject(err)) }) return } else { reject(`${promises} is not Array`) } }) } /** * 传入数组格局的`Promise`并返回新的`Promise`实例,胜利与失败取决第一个的实现形式 * * @static * @param {*} promises * @return {*} * @memberof Promise */ static race(promises) { return new Promise((resolve, reject) => { if (Array.isArray(promises)) { promises.forEach((promise, index) => { // 转换成Promise对象 Promise.resolve(promise).then((res) => { resolve(res) }, err => reject(err)) }) } else { reject(`${promises} is not Array`) } }) } // 推入微工作 _nextTick(fn) { if (typeof MutationObserver !== 'undefined') { // 浏览器 // 这块能够独自拿进去共用,防止不必要的开销,不然每次都须要生成节点。 const observer = new MutationObserver(fn) let count = 1 const textNode = document.createTextNode(String(count)) observer.observe(textNode, { characterData: true }) textNode.data = String(++count) } else if (typeof process.nextTick !== 'undefined') { // node process.nextTick(fn) } else { setTimeout(fn, 0) } } // 胜利resolve _resolve(value) { // 状态确定了,就不再发生变化了 if (this._state !== 'pending') return // 下面示例外面其实返回的是一个Promise,而不是间接返回的值,所以,这里咱们须要做一个非凡解决。 // 就是如果resolve()的如果是Promise的对象,咱们须要解析Promise的后果,而后在把值传给resolve if (typeof value === 'object' && typeof value.then === 'function') { // 咱们能够把以后_resolve办法传递上来,因为then办法中的参数,一经下个Promise resolve,便会执行then办法对应的参数,而后把对应的值传入。 // 这样就能取到Promise中的值 // this._resove => obj.onFulfilled?.(this._value) // this._reject => obj.onRejected?.(this._value) value.then(this._resolve.bind(this), this._reject.bind(this)) return } // 通过打印测试,如果间接在线程里进行resolve, 状态跟值如同是间接就扭转了,并没有执行完主流程,在执行微工作的时候进行批改的。 // 所以把状态扭转和值的批改移出了微工作,只有在走回调的时候才通过微工作进行解决 this._state = 'fulfilled' this._value = value // 推入微工作 this._nextTick(() => { this._queue.forEach((obj) => { // 应用try catch 来捕捉onFulfilled存在函数外部谬误的状况 try { // 承受onFulfilled返回值,如果不存在,把this._value往下传递 const val = obj.onFulfilled ? obj.onFulfilled(this._value) : this._value // reoslve这个值,此时 onFulfilled 是以后Promise then办法中的第一个参数: Promise.then((res) => {consolle.log(res)}) // obj.resolve是新的Promise的resolve函数,这样就把then办法中的返回值传给下一个Promise obj.resolve(val) } catch (e) { obj.reject(e) } }) }) } // 失败reject _reject(error) { if (this._state !== 'pending') return this._state = 'rejected' this._value = error this._nextTick(() => { this._queue.forEach((obj) => { try { // 用户传入的函数外部谬误捕捉 if (obj.onRejected) { const val = obj.onRejected(this._value) // 以后 reject执行结束之后,会返回新的Promise,应该是能失常resolve的,所以这里要用 resolve, 不应该持续应用reject来让下个Promise执行失败流程 obj.resolve(val) } else { // 递归传递reject谬误 obj.reject(this._value) } } catch (e) { obj.reject(e) } }) }) }}
残缺演示成果
博客原文地址
本我的项目残缺代码:GitHub
以上就是Promise的实现计划,当然这个跟残缺的Promises/A+标准
是有区别的。这里只是用做于学习之用。
QQ群 | 公众号 |
---|---|
前端打杂群 | 冬瓜书屋 |
我本人新创建了一个互相学习的群,无论你是筹备入坑的小白,还是半路入行的同学,心愿咱们能一起分享与交换。
QQ群:810018802, 点击退出