需要具备基础:
[
' 同步和异步 ',
' 微任务与宏任务 ',
' 递归调用 ',
' 链式调用 ',
' 回调函数 ',
' 发布订阅 ',
' 描述符get ',
' 错误类型 ',
' try捕获 ',
' ES6 '
]
必备的基础知识是要熟练掌握的。 其次,得先明白Promise怎么用,才能知道怎么写。
本源码遵守的的是Promises/A+规范,另外还有ECMA-Promise规范,但实际上两者差别不大,如果你想写JS解释器那就只能看ECMA标准的Promise了😏。
直接上代码了,真是没啥可说的,该注释的都注释了。
(function(){
const PENDING = Symbol('PENDING'); //等待状态
const FULFILLED = Symbol('FULFILLED'); //成功状态
const REJECTED = Symbol('REJECTED'); //失败状态
const citeError = 'Chaining cycle detected for promise #<Promise>'
const allError = 'is not a Array'
const resolvePromise = (promise2, x, resolve, reject) => {
//called变量是为了防止其他Promise库中没有对成功失败状态进行固定,
//导致
let called;
//此处ES6得Promise规范是没有这个报错的
//可以不判断,自己看情况
if (promise2 === x) {
return reject(new TypeError(citeError))
}
//在这里没有使用 x instanceof Promise 是因为要兼容其他的Promise库
//其他人编写的Promise类库那就肯定不会和我们自己的Promise类有什么关联了啊
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
try {
//防止x的then属性使用get定义,get本身也是一个方法,执行过程可能报错
let then = x.then
if (typeof then !== 'function') {
//防止x的then属性使用get定义,get本身也是一个方法,执行过程可能报错
// 第二次执行可能报错,所以使用call来执行保险
then.call(x, y => {
if (called) return; //确保状态固定
called = true;
//递归调用,确保最终拿到非Promise类型值
resolvePromise(promise2, y, resolve, reject)
}, r => {
if (called) return;//确保状态固定
called = true;
//错误直接输出,不必过滤到基础数据值
reject(r)
})
} else {
//如果then属性值不是一个函数,则证明x不是一个Promise类
resolve(x)
}
} catch (err) {
//x.then过程一旦报错,直接输出reject
if (called) return;//确保状态固定
called = true;
reject(err)
}
} else {
//基础数据类型直接输出resolve
resolve(x)
}
}
class Promise {
constructor(executor) {
if(typeof executor !== 'function'){
throw new TypeError('Promise resolver #<Object> is not a function')
}
this.status = PENDING; //实例的状态,默认为等待中
this.value = undefined; //成功状态的值
this.reason = undefined; //失败状态的值
this.onFulfilledCallbacks = []; //成功回调订阅容器
this.onRejectedCallback = []; //失败回调订阅容器
//成功的触发器
this.resolve = (value) => {
//如果成功回调的value为promise实例,则把自己的成功失败的触发器
//传递给value的then,如果value的then依然是promise实例。则会循环往复
//直到拿到非promise实例的值
//注意不要把此处的处理和onFulfilled和onRejected的return值概念相混淆。
//这个实例最终执行时还会进入异步,所以在此处不需要异步执行
if (value instanceof Promise) {
return value.then(this.resolve, this.reject)
}
//触发器一定要异步调用,这是规定
setTimeout(()=>{
//只有在等在状态才可以执行,因为状态一定确定,就不能在修改以及执行
if (this.status === PENDING) {
this.value = value //更新值
this.status = FULFILLED //更新状态
//把订阅容器预存的onFulfilled函数依次执行
for (let fn of this.onFulfilledCallbacks) {
fn()
}
}
})
}
//失败的触发器
this.reject = (reason) => {
//触发器一定要异步调用,这是规定
setTimeout(()=>{
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
//这段代码处理了一个问题,就是当Promise实例没有指定失败回调,
//但是却执行了reject,导致错误信息丢失,这是不应该的,我们需要
//抛出一个错误提示,但最好也不要阻碍程序运行。
if (this.onRejectedCallback.length === 0) {
console.error(reason)
}
for (let fn of this.onRejectedCallback) {
fn()
}
}
})
}
//如果传入构造器的函数执行出现错误,需要使用reject的方式输出
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
//添加一个可以得到类型的属性,添加这条属性可以使用
// Object.prototype.toString.call(实例) = '[object Promise]'
get [Symbol.toStringTag]() {return "Promise"}
//通过then方法添加成功和失败的回调
then(onFulfilled, onRejected) {
//有了这两个方法,可以实现then没参数的情况下无限传递
//实例.then().then().then()....
if (typeof onFulfilled !== 'function') {
onFulfilled = val => val
}
if (typeof onRejected !== 'function') {
onRejected = err => { throw err }
}
//then方法必须返回一个新的Promise实例,已实现链式调用
//Jquery的链式调用通过 renturn this,注意领悟精神
let _resolve, _reject;
const promise2 = new Promise((resolve, reject) => {
//注意,这部分还是同步执行的,吧resolve,reject外置,作用是
//可以把外部的实例拿到值时,通过_resolve和_reject触发器传入给promise2
_resolve = resolve
_reject = reject
})
//如果已成功,不必加入订阅容器,直接执行
if (this.status === FULFILLED) {
try {
//x 为 onFulfilled()的返回值
//注意此处的返回值x有可能是Promise实例的,必须处理成
//非Promise实例的值,由resolvePromise函数处理
let x = onFulfilled(this.value)
resolvePromise(promise2, x, _resolve, _reject)
} catch (err) {
//如果onFulfilled的函数抛出异常,直接把错误对象返回
_reject(err)
}
}
//如果已失败,不必加入订阅容器,直接执行
if (this.status === REJECTED) {
try {
// 同上
let x = onRejected(this.reason)
resolvePromise(promise2, x, _resolve, _reject)
} catch (err) {
//同上
_reject(err)
}
}
//如果是等待中,需要加入订阅容器,等待触发器触发时再来调用
if (this.status === PENDING) {
//和上面意思一样,只不过这部分被预存了,联想发布订阅
this.onFulfilledCallbacks.push(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, _resolve, _reject)
} catch (err) {
_reject(err)
}
})
this.onRejectedCallback.push(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, _resolve, _reject)
} catch (err) {
_reject(err)
}
})
}
return promise2
}
catch(errCallback) {
return this.then(null, errCallback)
}
finally(callback) {
return this.then(
value => Promise.resolve(callback()).then(() => value),
//这块为什么用Promise.resolve千万别迷糊,使用Promise.reject
//也是完全可以的,只不过在后面的你需要这么写
//.then(null, () => { throw reason },少传个参数显得高档。。
reason => Promise.resolve(callback()).then(() => { throw reason })
)
}
static resolve(value) {
//这个判断可以不写的,但是写了可以调高一点点性能
if(value instanceof Promise) return value
return new Promise(resolve => {
resolve(value)
})
}
static reject(reason) {
//reason无论什么数据类型,都要直接使用reject输出
//不能像Promise.resolve方法那样有个优化性能的判断
//因为那样有可能触发onFulfilled,那就违背原则了
return new Promise((resolve, reject) => {
reject(reason)
})
}
static all(promises) {
//这里注意,我是图方便限定格式为数组,实际ES6的参数设定是必须是
//一个可迭代对象
if (!(promises instanceof Array)) {
throw new Error(`${typeof promises} ${promises} ${allError}`)
}
//数组为空,直接返回一个Promise.resolve
if (promises.length === 0) {
return Promise.resolve(promises)
}
return new Promise((resolve, reject) => {
setTimeout(() => {
let arr = [];
let i = 0;
let processData = (index, data) => {
arr[index] = data;
if (++i === promises.length) {
resolve(arr)
}
}
promises.forEach((item, i) => {
if (item instanceof Promise) {
item.then(data => {
processData(i, data)
}, reject)
} else {
processData(i, item)
}
})
})
})
}
static race (promises) {
//如果传的迭代是空的,则返回的 promise 将永远等待。
//这个特性可以用于停止Promise链。
if(promises === undefined){
return new Promise(()=>{})
}
if (!(promises instanceof Array)) {
throw new Error(`${typeof promises} ${promises} ${allError}`)
}
if (promises.length === 0) {
return Promise.resolve(promises)
}
return new Promise((resolve, reject) => {
for (let p of promises) {
if(!(p instanceof Promise)){
p = Promise.resolve(p)
}
// 只要有一个实例率先改变状态,Promise的状态就固定
p.then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
}
try{
module.exports = Promise
}catch(e){
window.Promise = Promise
}
})()