Promise 可以看作一个异步操作的占位符, 表示一个异步操作的最终状态 (完成或失败), 以及其返回的值, 是目前流行 JavaScript 异步编程解决方案之一.
网上关于 Promise 用法的文章汗牛充栋, 作者在本文也不赘述了. Promise 用久了难免会对其内部运行原理感到好奇, 作者曾试着遵循 Promise/A + 规范自己写一个 promise 的补丁, 无奈野心被能力实力打脸, 冷静之后 去 GitHub 上找 promise 的 polyfill 学习观摩, promise-polyfill 作为一个轻量级, 设计巧妙的 polyfill 让作者大开眼界, 仔细观摩学习之后, 写成本篇小记与大家分享, 错漏之处恳求指点.
一 . 框架
作者将不会按照 promise-polyfill 的源码一行行进行讲解, 而为了阅读方便将使用 ES6/7 的语法重新搭好一个框架, 慢慢往里面塞 promise-polyfill 核心代码
我们将搭好下面的代码架构, 先实现除了 constructor,then 的其他方法找找感觉, 然后慢慢往里加 promise-polyfill 的核心代码, 不耐烦的同学可以直接跳到下文 核心 部分阅读
- class Promise {
- constructor (executor) { ... }
- then (onFulfilled, onFulfilled) { ... }
- catch (onRejected) { ... }
- finally(callback) { ... }
- static resolve(value) { ... }
- static reject(reason) { ... }
- static all(iterable) { ... }
- static race(iterable) { ... }
- }
先假装已经实现了 constructor,then 的核心功能
- 1. catch
- /**
- * catch 很简单, 就是 then(null, onRejected) 的语法糖
- */
- catch (onRejected) {
- return this.then(null, onRejected)
- }
- 2.finally
- /***
- ** finally 总是执行回调
- ***/
- finally (callback) {
- return this.then(
- function(value) {
- return Promise.resolve(callback()).then(() => value)
- },
- function(reason) {
- return Promise.resolve(callback()).then(() => Promise.reject(reason))
- }
- )
- }
- // tips
- prom.finally(() => { «statements» })
等于
- prom.then(result => {
- «statements»
- return result
- }, error => {
- «statements»
- throw error
- })
- 3. resolve
- /**
- * 静态 resolve 方法返回一个 Promise 对象
- */
- static resolve (value) {
- if (value && typeof value === 'object' && value.constructor === Promise) {
- return value
- }
- return new Promise(function(resolve, reject) {
- resolve(value)
- })
- }
- 4. reject
- static reject (reason) {
- return new Promise(function (resolve, reject) {
- reject(reason)
- })
- }
- 5. all
- /**
- **** 简单起见, 并未按照 promise-polyfill 的 all 方法, 面试中也经常出现要求实现一个 Promise.all 方法
- **/
- static all (iterable) {
- return new Promise((resolve, reject) => {
- let count = 0, result = [], len = iterable.length
- if (len === 0) {
- resolve(result)
- }
- function resolver (i) {
- return function (x) {
- result[i] = x
- count++
- if (count === i) {
- resolve(i)
- }
- }
- }
- for(let i = 0; i <len; i++) {
- Promise.resolve(iterable[i]).then(resolver(i), reject)
- }
- })
- }
- 6. race
- /**
- **** 谁的异步时间短, 谁先处理, 其余忽略
- ***/
- static race(iterable) {
- return new Promise((resolve, reject) => {
- for (let i = 0, len = iterable.length ; i <len; i++ ) {
- Promise.resolve(iterable[i]).then(resolve, reject)
- }
- })
- }
二. 核心
为了避免不必要的干扰, 省略代码将以 @code_{function_name}_{number} 标号. 部分优化, 兼容代码将省略, 例如对 setTimeout 的优化, 将直接使用 setTimeout, 并不会影响代码逻辑
- 1. constructor
- /**
- * @constructor 初始化代码
- * 1. 初始化代码进行常规的类型检查和属性赋值
- * 2. @_state 状态
- * penging <===> 0
- * fulfilled <===> 1
- * rejected <===> 2
- * <Promise> <===> 3 (自定义的 state, 用于取 < Promise > 内部的值, 在下文详解)
- * 3.@_handled 标记当前 Promise 实例的结果是否被处理过, 初始化为 false
- * 3.@_deferreds 存放在 then 中的注册的函数, 元素形式 {onFulfilled, onRejected,promise}
- * 4.@doResolve 详见下文
- **/
- constructor (executor) {
- if (!(this instanceof Promise))
- throw new TypeError('Promises must be constructed via new')
- if (typeof fn !== 'function') throw new TypeError('not a function')
- /** @type {!number} */
- this._state = 0
- /** @type {!boolean} */
- this._handled = false
- /** @type {Promise|undefined} */
- this._value = undefined
- /** @type {!Array<!Function>} */
- this._deferreds = []
- doResolve(fn, this)
- }
- /***
- ** @doResolve 分发 self 给 resolve 和 reject 进行状态转换
- ** @done 确保当前实例的状态由 penging=>fulfilled 或者 penging=>rejected 只发生一次
- ** @resolve, reject 全局方法, 处理状态转换
- **/
- function doResolve (fn, self) {
- var done = false
- try {
- fn(
- function (value) {
- if (done) return
- done = true
- resolve(self, value)
- },
- function (reason) {
- if (done) return
- done = true
- reject(self, reason)
- }
- )
- } catch (ex) {
- if (done) return
- done = true
- reject(self, ex)
- }
- }
2. then
executor 执行完同步代码, 紧接着将在 then 中注册方法 onFulfilled,onRejected 挂载到一个 deferred 对象上
- // 空函数
- const noop = function() {}
- /**
- ** 1. 先创建一个 Promise 实例 prom
- ** 2. new Handler(onFulfilled, onRejected, prom) 创建一个 deferred 对象挂载 then 上注册的方法和下个 promise 实例 prom <deferred: {onFulfilled, onRejected, promise}>
- ** 3. handle 在此将 deferred 加入到当前 promise 实例的 _deferreds 队列上
- ** 4. 返回 Promise 的实例 prom, 供后面的 then,catch, 继续注册方法
- ** 5. 返回一个新 Promise 的实例是为了确保 pending => fulfilled 或者 pending => rejected
- ***/
- then(onFulfilled, onRejected) {
- let prom = new this.constructor(noop)
- handle(this, new Handler(onFulfilled, onRejected, prom))
- return prom
- }
- /**
- *** @Handler 创建一个 deferred <deferred: {onFulfilled, onRejected, promise}>
- ** onFulfilled, onRejected 不是函数则赋值为 null, 方便在 handle 中统一处理
- **/
- function Handler (onFulfilled, onRejected, promise) {
- this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null
- this.onRejected = typeof onRejected === 'function' ? onRejected : null
- this.promise = promise
- }
- /***
- *** then 方法中调用 handle 就只是为了将 deferred 添加到当前 Promise 实例的_deferreds 上, 等待最后处理 Promise 的返回值
- ***/
- function handle (self, deferred) {
- // ...@code_handle_1
- if (self._state === 0) {
- self._deferreds.push(deferred)
- return
- }
- // ...@code_handle_2
- }
3. Promise 结果处理
我们先将假设 Promise 的状态已发生改变, 如何改变的先不表. promise-polyfill 把结果统一在 finale 中处理
- /***
- **** @function finale
- *** 1. 当前实例在 rejected 状态且未注册 deferred, 注册一个定时器在下个事件循环中检查 Promise 的值是否被调用过
- *** 2. 循环调用调用 handle, 用当前实例的_deferreds 队列中注册的方法处理 promise 的返回值
- *** 3. 释放当前实例的_deferreds 的空间
- ***/
- function finale (self) {
- if (self._state === 2 && self._deferreds.length === 0) {
- setTimeout(function () { // 原代码有优化, 此处代码改动, 但逻辑未变
- if (!self._handled) {
- if (typeof console !== 'undefined' && console) {
- console.warn('Possible Unhandled Promise Rejection:', self.__value)// eslint-disable-line no-console
- }
- }
- })
- }
- for (var i = 0, len = self._deferreds.length; i <len; i++) {
- handle(self, self._deferreds[i])
- }
- self._deferreds = null
- }
- /**
- ** @function handle
- ** 1. while 循环检查当前实例的值是否是一个 Promise 实例, self = self._value 直到 self 的值不是 promise
- ** 2. self._state === 0, 主要用在 then 方法中使用在添加 deferred 对象
- ** 3. 标识当前 promise 实例已调用
- ** 4. 启动一个定时器, 注册方法在下个循环中处理当前 promise 结果
- **/
- function handle (self, deferred) {
- while (self._state === 3) {
- self = self._value
- }
- if (self._state === 0) {
- self._deferreds.push(deferred)
- return
- }
- self._handled = true
- setTimeout(function () {
- // 根据当前 promise 状态, 取挂载在 deferred 对象上的对应方法 (在 then 方法中注册的方法)
- var cb = self._state === 1 ? deferredeferred.onFulfilled : deferred.onRejected
- // 如果 cb==null, 则表示未在 then 中注册方法, 直接根据当前 promise 的状态, 调用 resolve 或 reject, 将当前值放到下一个 then 注册的方法中, 退出当前调用栈
- if (cb === null) {
- (self._state === 1 ? resolve : reject)(deferred.promise, self._value)
- return
- }
- var ret
- try {
- // 由 then 中注册的方法调用当前 promise 的值, 返回值放入 ret
- ret = cb(self._value)
- } catch (e) {
- // 如果有异常将异常结果作为当前实例 then 注册方法的值传入下个 promise 供 then 注册的 onRejected 方法调用
- reject(deferred.promise, e)
- return
- }
- // 将 ret 作为当前 promise 实例 then 方法中的值传入下个 promise 的 then 注册的 onFulfilled 方法调用
- resolve(deferred.promise, ret)
- })
- }
4. 全局 resolve,reject 函数
非 Promise 实例方法, 用作处理 Promise 的状态转移
- /**
- ******@function resolve
- ** 处理 penging ===> fulfilled/rejected 的状态转移
- ** 1. 检查 Promise 实例是否 resolve 调用自身, 报错 转 rejected
- ** 2. 检查 newValue 返回值是否是一个 Promse 实例, 设置私有状态 3(最后将在 handle 中解析成非 promise 值), 传递给 finale 进一步处理
- ** 3. 检查 newValue 是否是一个 thenable 类型, 调用 doResolve
- ** 4. 非 Promise 的值, state 设为 1, 传递给 finale 进一步处理
- ***/
- function resolve (self, newValue) {
- try {
- // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
- // 对应规范 2.3.1: 如果 promise(self) 和 x(newValue) 引用同一个对象, 请以 promisea TypeError 为理由拒绝.
- if (newValue === self) { throw new TypeError('A promise cannot be resolved with itself.') }
- if (
- newValue &&
- (typeof newValue === 'object' || typeof newValue === 'function')
- ) {
- var then = newValue.then
- if (newValue instanceof Promise) {
- self._state = 3
- self._value = newValue
- finale(self)
- return
- } else if (typeof then === 'function') {
- /**
- ** 对应规范 2.3.3.3 如果 then 是函数, 请使用 x as this,first 参数 resolvePromise 和第二个参数调用它 rejectPromise
- **/
- doResolve(bind(then, newValue), self)
- return
- }
- }
- self._state = 1
- self._value = newValue
- finale(self)
- } catch (e) {
- reject(self, e)
- }
- }
- /**
- ** Polyfill for Function.prototype.bind
- **/
- function bind (fn, thisArg) {
- return function () {
- fn.apply(thisArg, arguments)
- }
- }
- /***
- * 修改状态, 传递给 finale 进一步处理
- ***/
- function reject (self, newValue) {
- self._state = 2
- self._value = newValue
- finale(self)
- }
结尾
总算水完了, 看着好像也没啥干货, 错漏之处恳求指点.
来源: https://juejin.im/post/5c838860f265da2ddd4a8ff5