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)
}
复制代码
结尾
总算水完了,看着好像也没啥干货,错漏之处恳求指点。