当前位置: 首页 > news >正文

福州有网站建设的公司网站运营怎么做

福州有网站建设的公司,网站运营怎么做,黄山旅游攻略二日游山上住一晚,世界500强企业市值排名前言 这可能是手写promise较清晰的文章之一。 由浅至深逐步分析了原生测试用例,以及相关Promise/A规范。阅读上推荐以疑问章节为切入重点,对比Promise/A规范与ECMAScript规范的内在区别与联系,确定怎样构建异步任务和创建promise实例。然后开…

在这里插入图片描述

前言

  这可能是手写promise较清晰的文章之一。

  由浅至深逐步分析了原生测试用例,以及相关Promise/A+规范。阅读上推荐以疑问章节为切入重点,对比Promise/A+规范与ECMAScript规范的内在区别与联系,确定怎样构建异步任务和创建promise实例。然后开始手写章节,过程中代码与测试可参考 promise-coding 仓库。

  也试着回答以下关键问题。

  • 什么是广义对象?
  • 如何检验promise类型?
  • promisethenable两类型有何区别?

疑问

  如果不太清楚Promise,建议参考《ECMAScript 6 入门》,预习下Promise相关用法知识。

  除此之外,对规范也要有大致认识,我们将根据几个疑问来细致阐述。

什么是 Promise/A+ 规范?

  Promise有多种社区规范,例如 Promise/A、Promise/B、Promise/D 和 Promises/KISS 等。

  Promise/A+ 在Promise/A之上,规范了术语并拓展了参数行为,省略了一些有问题的部分。

  Promise/A+有很多实现,例如第三方库 q、when 和 bluebird 等。实际上任何Promise通过测试,都被认为是对Promise/A+规范的一种实现。

Promise/A+规范官方测试用例为 promises-aplus-tests

原生 Promise 实现了 Promise/A+?

  在Promise/A+规范 The ECMAScript Specification 章节中。

The ECMAScript Specification
...
Largely due to the actions of the Promises/A+ community, the Promise global specified by ECMAScript and present in any conforming JavaScript engine is indeed a Promises/A+ implementation!

  叙述了JavaScript引擎中的Promise也是对Promise/A+规范的一种实现。

  为什么呢?

  Promise/A+规范内容上仅说明了Promise状态和then方法。

  ECMAScript规范不仅规定Promise为构造函数,还添加了静态方法,例如Promise.resolvePromise.allPromise.race等,新增了原型方法Promise.prototype.catchPromise.prototype.finally等。其中Promise.prototype.then相关内容,则是根据Promise/A+规范转化而来。

  我们知道,JavaScript就是对ECMAScript规范的一种实现,而ECMAScript规范中Promise.prototype.then相关内容又继承了Promise/A+规范。

  那么可以说,JavaScript引擎中的Promise,即原生Promise,就是对Promise/A+规范的一种实现。

如何构建异步任务?

  Promise/A+规范规定then方法接收两个参数。

promise.then(onFulfilled, onRejected)

  在 2.2.4 小结中明确了参数必须以异步形式运行。

2.2.4. onFulfilled or onRejected must not be called until the execution context stack contains only platform code.

  注解 3.1 补充可用宏任务setTimeoutsetImmediate,或者微任务MutationObserver(浏览器环境)和process.nextTicknode环境)达到异步。

3.1. ...In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a "macro-task" mechanism such as setTimeout or setImmediate, or with a "micro-task" mechanism such as MutationObserver or process.nextTick.

  综上所述,Promise/A+规范仅规定了参数以异步形式运行,并未规定是宏任务还是微任务。

注意V8引擎内部为微任务,考虑一致性推荐 queueMicrotask 创建微任务,兼容性相对较好

如何创建 promise?

  Promise/A+规范并未提及如何创建promise

  ECMAScript6规范发布以来,多数是以构造函数方式创建promise

new Promise(executor)

  实际上在此之前,还流行一种Deferred方式,例如 JQuery.Deferred。

$.Deferred()

  我们以定时器为例,对比下两者差异。

// ECMAScript6 Promise
const promise = new Promise(resolve => {setTimeout(() => {resolve(1)}, 1000)
})promise.then(v => {console.log(v) // 1
})// JQuery Deferred
const deferred = $.Deferred()deferred.promise().then(v => {console.log(v) // 1
})setTimeout(() => {deferred.resolve(1)
}, 1000)

  你也注意到了吧,Deferred方式相对更加灵活,可以在任何时机修改状态。而Promise方式自由度减少了很多,不仅代码层级多了一层,而且只能在函数参数中修改状态。

  可能你会问,那为什么TC39放弃了Deferred,而决定了Promise构造器方式呢?

  Deferred方式存在一个较严重的缺陷,即在业务流程阶段,不容易捕获异常。

const deferred = $.Deferred()deferred.promise().catch(v => {console.log(v)
});(function () {throw new Error() // Uncaught Errordeferred.resolve(1)
})()

  如果想让promise捕获异常,业务代码可修改为。

;(function () {try {throw new Error()} catch (error) {deferred.reject(error)}deferred.resolve(1)
})()

  而Promise构造器方式则非常容易。

const promise = new Promise(resolve => {throw new Error()resolve(1)
})promise.catch(v => {console.log(v) // Error
})

  两相比较下ECMAScript6确定了以构造器方式创建promise

个人认为Deferred更多是一个发布订阅器,而Promise则相对更加强大,是一个异步流程解决方案,ECMAScript6规范将其独立为一个模块是相当合理的

手写

  Promise/A+更多地是规范了算法逻辑,并未规定语法层面的实现方式。

  我们可以参考原生Promise语法,并结合简单用例,手写以符合Promise/A+规范。

注意Promise/A+规范相关内容将特别标注

实例初始属性

  原生创建Promise实例。

new Promise(() => {})
// {
//   [[PromiseState]]: 'pending',
//   [[PromiseResult]]: undefined,
// }

  相关特征包括。

  • Promise为构造函数
  • 默认状态为pending,默认结果为undefined
  • 三种状态分别为等待态pending、解决态fulfilled和拒绝态rejected——「Promise/A+ 2.1」

  代码编写如下,其中属性[[PromiseState]]用于保存状态,[[PromiseResult]]用于保存结果。

const PromiseState = '[[PromiseState]]'
const PromiseResult = '[[PromiseResult]]'const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'class Promise {[PromiseState] = PENDING;[PromiseResult] = undefined
}

ES2020规范 proposal-class-fields 允许实例属性定义在类内部的最顶层,相对更加清晰简洁

executor 执行器

  原生Promise传参函数。

new Promise(function executor() {console.log(1) // 1
})
console.log(2) // 2new Promise((resolve, reject) => {resolve(3)
})
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 3,
// }new Promise((resolve, reject) => {reject(4)
})
// {
//   [[PromiseState]]: 'rejected',
//   [[PromiseResult]]: 4,
// }

  相关特征包括。

  • 实例创建过程中参数executor将同步执行
  • 执行器executor包括resolvereject两个函数参数,resolve执行实例状态修改为解决态,reject执行实例状态修改为拒绝态

  以下为优化代码,注意私有方法用箭头函数,可将内部this指向实例对象。

class Promise {...#resolve = value => {this[PromiseState] = FULFILLEDthis[PromiseResult] = value}#reject = reason => {this[PromiseState] = REJECTEDthis[PromiseResult] = reason}constructor(executor) {executor(this.#resolve, this.#reject)}
}

ES2020规范 proposal-class-fields 允许实例定义私有属性或方法,仅可在类内部使用,外部无法使用

状态不可变性

  原生Promise修改状态。

new Promise((resolve, reject) => {resolve(1)resolve(2)
})
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 1,
// }new Promise((resolve, reject) => {resolve(3)reject(4)
})
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 3,
// }

  相关特征包括。

  • 处于解决态或者拒绝态,一定不能再修改为任何状态——「Promise/A+ 2.1.2 / 2.1.3」
  • 处于等待态的时候,可能变为解决态或者拒绝态——「Promise/A+ 2.1.1」

  代码优化为。

#resolve = value => {if (this[PromiseState] === PENDING) {this[PromiseState] = FULFILLEDthis[PromiseResult] = value}
}
#reject = reason => {if (this[PromiseState] === PENDING) {this[PromiseState] = REJECTEDthis[PromiseResult] = reason}
}

方法传参

  原生Promisethen方法传参。

const p = new Promise((resolve, reject) => {resolve()
})p.then(undefined, undefined)

  相关特征包括。

  • promise必须有then方法,且接收两个参数onFulfilledonRejected——「Promise/A+ 2.2」
  • onFulfilledonRejected都是可选参数,若不是函数,必须被忽略——「Promise/A+ 2.2.1」
  • onFulfilledonRejected一定被作为普通函数调用——「Promise/A+ 2.2.5」

  代码修改为。

class Promise {...then(onFulfilled, onRejected) {onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {}onRejected = typeof onRejected === 'function' ? onRejected : () => {}}
}

参数为非函数时,为保证可被调用,暂时返回普通函数

then 方法

  原生Promise执行then方法。

const p1 = new Promise((resolve, reject) => {resolve(1)
})
p1.then(v => {console.log(v) // 1
})const p2 = new Promise((resolve, reject) => {reject(2)
})
p2.then(undefined, v => {console.log(v) // 2
})

  相关特征包括。

  • 如果onFulfilledonRejected是一个函数,必须在promise被解决或被拒绝后调用,且promise值或原因作为第一个参数——「Promise/A+ 2.2.2 / 2.2.3」

  代码修改为。

then(onFulfilled, onRejected) {...if (this[PromiseState] === FULFILLED) {onFulfilled(this[PromiseResult])}if (this[PromiseState] === REJECTED) {onRejected(this[PromiseResult])}
}

异步 executor

  目前代码并未完全符合「Promise/A+ 2.2.2 / 2.2.3」规范,例如executor为异步情况时,还会存在一些问题。

const p = new Promise((resolve, reject) => {setTimeout(() => {resolve(1)}, 1000)
})p.then(v => {console.log(v)
})

控制台没有打印任何内容

  为什么呢?

  实例p在创建完成后,还处在等待态。紧接着执行then方法,then方法内部没有等待态相关逻辑,也就没有任何操作。1sresolve执行,也仅仅是将p状态修改为解决态。

  如何解决呢?

  可以在等待态就保存onFulfilledonRejected函数,在resolve修改状态时再执行。

  代码优化为。

class Promise {...#onFulfilledCallBack = undefined#onRejectedCallBack = undefined#resolve = value => {if (this[PromiseState] === PENDING) {...this.#onFulfilledCallBack?.(this[PromiseResult])}}#reject = reason => {if (this[PromiseState] === PENDING) {...this.#onRejectedCallBack?.(this[PromiseResult])}}...then(onFulfilled, onRejected) {...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBack = onFulfilledthis.#onRejectedCallBack = onRejected}}
}

?.ES2020规范中 proposal-optional-chaining 可选链操作符

多次调用 then

  原生Promise多次调用then方法。

const p = new Promise((resolve, reject) => {setTimeout(() => {resolve()}, 1000)
})p.then(() => {console.log(1) // 1
})p.then(() => {console.log(2) // 2
})p.then(() => {console.log(3) // 3
})

  相关特征包括。

  • then方法函数参数按语法顺序执行
  • 同一promisethen方法可能被多次调用——「Promise/A+ 2.2.6」

  代码优化如下,注意为了保证顺序,两数组内函数都是先进先出。

class Promise {...#onFulfilledCallBacks = []#onRejectedCallBacks = []#resolve = value => {if (this[PromiseState] === PENDING) {...while (this.#onFulfilledCallBacks.length) {this.#onFulfilledCallBacks.shift()(this[PromiseResult])}}}#reject = reason => {if (this[PromiseState] === PENDING) {...while (this.#onRejectedCallBacks.length) {this.#onRejectedCallBacks.shift()(this[PromiseResult])}}}...then(onFulfilled, onRejected) {...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(onFulfilled)this.#onRejectedCallBacks.push(onRejected)}}
}

返回 promise

  原生Promise返回值。

const p = new Promise(() => {})p.then()
// {
//   [[PromiseState]]: 'pending',
//   [[PromiseResult]]: undefined,
// }

  相关特征包括。

  • then方法必须返回一个新promise——「Promise/A+ 2.2.7」

  代码暂时修改为。

then(onFulfilled, onRejected) {...if (this[PromiseState] === PENDING) {...}const promise = new Promise(() => {})return promise
}

函数参数返回值

  原生Promise函数参数onFulfilled返回数值。

const p = new Promise((resolve, reject) => {resolve()
})p.then(() => {return 1
})
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 1,
// }

  相关特征包括。

  • 如果onFulfilledonRejected返回一个值x,运行promise解决程序——「Promise/A+ 2.2.7.1」
  • 如果x不是一个对象或函数,用x解决promise——「Promise/A+ 2.3.4」

  何为promise解决程序呢?

  「Promise/A+ 2.3」叙述是一个抽象操作,可表示为[[Resolve]](promise, x)。其中主要根据x类型,决定新promise的状态和结果。

  比如x不是一个对象或函数,例如数值,则新promise状态将确定为解决态,结果为x,即用x解决promise

// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: x,
// }

  那么如何在onFulfilledonRejected返回数值x时,又修改新promise状态和结果呢?

then(onFulfilled, onRejected) {...if (this[PromiseState] === FULFILLED) {const x = onFulfilled(this[PromiseResult])}...const promise = new Promise(() => {})return promise
}

  你可能想到了。

then(onFulfilled, onRejected) {...const promise = new Promise(() => {})if (this[PromiseState] === FULFILLED) {const x = onFulfilled(this[PromiseResult])promise.#resolve(x)}...return promise
}

  可修改状态也符合规范,但个人认为此方式存在一些缺陷。

  将实例属性resolve私有化,就是为了限制外部访问。以promise.#resolve访问,而非this.#resolve,已经处于外部访问的范畴了,思路上不是很合理。

  还有更好的办法吗?

  我们知道在executor执行器上,resolvereject两个参数也可修改状态。

  如果将if语句体迁移至executor内部,有没有可能呢?答案是可以的。

then(onFulfilled, onRejected) {...const promise = new Promise((resolve, reject) => {if (this[PromiseState] === FULFILLED) {const x = onFulfilled(this[PromiseResult])resolve(x)}...})return promise
}

if语句体在executor外部时,同步执行。在executor内部时,也是同步执行

  相关特征完全实现了吗?并没有。

  若executor为异步情况时,还存在一些问题。

const p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve()}, 1000)
})const p2 = p1.then(() => {return 2
})setTimeout(() => {console.log(p2)// {//   [[PromiseState]]: 'pending',//   [[PromiseResult]]: undefined,// }
}, 2000)

控制台打印内容与原生不一致

  为什么呢?

  实例p1处于等待态,执行then方法将onFulfilled保存至数组中。1sresolve执行,p1状态修改为解决态,紧接着取出运行onFulfilledp2状态无任何变化。

  我们可以在onFulfilled执行时,对返回值x稍加处理。

const promise = new Promise((resolve, reject) => {...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(() => {const x = onFulfilled(this[PromiseResult])resolve(x)})...}
})

处理函数

  为了统一处理不同类型x值,并严格实现规范「Promise/A+ 2.3」中各子章节。

  修改代码并创建resolvePromise函数,参数暂时为xresolve

class Promise {...then(onFulfilled, onRejected) {...const promise = new Promise((resolve, reject) => {if (this[PromiseState] === FULFILLED) {const x = onFulfilled(this[PromiseResult])resolvePromise(x, resolve)}...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(() => {const x = onFulfilled(this[PromiseResult])resolvePromise(x, resolve)})...}})return promise}
}function resolvePromise(x, resolve) {resolve(x)
}

  研读部分子章节。

2.3.1. If promise and x refer to the same object, reject promise with a TypeError as the reason.
2.3.2.2. If/when x is fulfilled, fulfill promise with the same value.
2.3.2.3. If/when x is rejected, reject promise with the same reason.

  可确认参数promisexresolvereject

const promise = new Promise((resolve, reject) => {if (this[PromiseState] === FULFILLED) {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)}...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(() => {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)})...}
})function resolvePromise(promise, x, resolve, reject) {resolve(x)
}

抛出异常

  原生Promise抛出异常。

const p = new Promise((resolve, reject) => {resolve()
})p.then(() => {throw new Error()
}).then(undefined, v => {console.log(v) // Error
})

  相关特征包括。

  • 如果onFulfilledonRejected抛出一个异常e,新promise为拒绝态且原因为e——「Promise/A+ 2.2.7.2」

  代码优化为。

const promise = new Promise((resolve, reject) => {if (this[PromiseState] === FULFILLED) {try {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)} catch (e) {reject(e)}}...
})

  类似地executor为异步情况时,也存在一些问题。

const p = new Promise((resolve, reject) => {setTimeout(() => {resolve()}, 1000)
})p.then(() => {throw new Error() // Uncaught Error
}).then(undefined, v => {console.log(v)
})

未捕获到异常

  为什么呢?

  实例p处于等待态,执行then方法将onFulfilled保存。1sresolve执行,p状态修改为解决态,紧接着取出onFulfilled,运行内部抛出了异常。

  代码优化为。

const promise = new Promise((resolve, reject) => {...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(() => {try {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)} catch (e) {reject(e)}})...}
})

异步任务

  原生Promise异步。

const p = new Promise((resolve, reject) => {resolve(1)
})console.log(2) // 2
p.then(v => {console.log(v) // 1
})
console.log(3) // 3

注意打印顺序2 3 1

  相关特征包括。

  • onFulfilledonRejected必须以异步形式运行——「Promise/A+ 2.2.4」

  代码简单修改为。

const queueTask = queueMicrotaskclass Promise {...then() {const promise = new Promise((resolve, reject) => {if (this[PromiseState] === FULFILLED) {try {queueTask(() => {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)})} catch (e) {reject(e)}}...})return promise}
}

  注意try...catch并不能捕获到异步函数内抛出的异常,例如。

try {setTimeout(() => {throw new Error() // Uncaught Error})
} catch (error) {console.log(error)
}

  那如何优化呢?

  我们可以将全部try...catch语句放到异步函数中。

const promise = new Promise((resolve, reject) => {if (this[PromiseState] === FULFILLED) {queueTask(() => {try {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)} catch (e) {reject(e)}})}...
})

  类似地executor为异步情况时,也存在一些问题。

const p = new Promise(resolve => {setTimeout(() => {console.log(1) // 1resolve(2)console.log(3) // 3}, 1000)
})p.then(v => {console.log(v) // 2
})

打印顺序1 2 3(原生打印顺序1 3 2

  为什么呢?

  onFulfilled没有以异步形式运行。

  代码修改为。

const promise = new Promise((resolve, reject) => {...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(() => {queueTask(() => {try {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)} catch (e) {reject(e)}})})...}
})

  合并重复代码。

const promise = new Promise((resolve, reject) => {const resolved = () => {queueTask(() => {try {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)} catch (e) {reject(e)}})}const rejected = () => {queueTask(() => {try {const x = onRejected(this[PromiseResult])resolvePromise(promise, x, resolve, reject)} catch (e) {reject(e)}})}if (this[PromiseState] === FULFILLED) {resolved()}if (this[PromiseState] === REJECTED) {rejected()}if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(resolved)this.#onRejectedCallBacks.push(rejected)}
})

参数优化

  原生Promise值穿透。

const p1 = new Promise((resolve, reject) => {resolve(1)
})
p1.then(undefined)
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 1,
// }const p2 = new Promise((resolve, reject) => {reject(2)
})
p2.then(undefined, undefined)
// {
//   [[PromiseState]]: 'rejected',
//   [[PromiseResult]]: 2,
// }

  相关特征包括。

  • 如果onFulfilled不是一个函数且原promise被解决,新promise必须也被解决,且值与原promise相同——「Promise/A+ 2.2.7.3」
  • 如果onRejected不是一个函数且原promise被拒绝,新promise必须也被拒绝,且原因与原promise相同——「Promise/A+ 2.2.7.4」

  代码优化如下。

then(onFulfilled, onRejected) {onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => valueonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }...
}

注意throw抛出异常将被try...catch捕获,进而拒绝新promise

类型

  如何处理不同类型x呢?

  还是参考规范「Promise/A+ 2.3」各子章节,以优化resolvePromise处理函数。

循环引用

  原生Promise循环引用。

const p1 = new Promise((resolve, reject) => {resolve()
})const p2 = p1.then(() => {return p2
})
// {
//   [[PromiseState]]: 'rejected',
//   [[PromiseResult]]: TypeError: Chaining cycle detected for promise #<Promise>,
// }

  相关特征包括。

  • 如果promisex引用同一对象,则拒绝promise,原因为一个TypeError——「Promise/A+ 2.3.1」

  代码优化为。

function resolvePromise(promise, x, resolve, reject) {if (promise === x) {return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}resolve(x)
}

传递性

  原生Promise函数参数onFulfilled返回promise

const p1 = new Promise((resolve, reject) => {resolve()
})
const p2 = new Promise((resolve, reject) => {reject(1)
})p1.then(() => {return p2
})
// {
//   [[PromiseState]]: 'rejected',
//   [[PromiseResult]]: 1,
// }

  相关特征包括。

  • 如果x是等待态,promise必须保持等待态,直到x被解决或被拒绝——「Promise/A+ 2.3.2.1」
  • 如果x是解决态,用相同的值解决promise——「Promise/A+ 2.3.2.2」
  • 如果x是拒绝态,用相同的原因拒绝promise——「Promise/A+ 2.3.2.3」

  也就是promise状态与x始终都保持一致。

  可能会存在x初始为等待态,然后又转变为解决态或拒绝态。过程中两者状态始终一致,若x状态转变,promise状态也将转变。

  那如何知道x状态转变呢?答案就是then方法。

x.then(onFulfilled, onRejected)

  x转变为解决态时将运行onFulfilled,转变为拒绝态时将运行onRejected

  那我们就可在onFulfilledonRejected内部去修改promise状态。

  代码优化为。

function resolvePromise(promise, x, resolve, reject) {...if (x instanceof Promise) {x.then(value => {resolve(value)}, reason => {reject(reason)})} else {resolve(x)}
}

  简化为。

function resolvePromise(promise, x, resolve, reject) {...if (x instanceof Promise) {x.then(resolve, reject)} else {resolve(x)}
}

广义对象

  何为广义对象呢?

  能添加属性或方法的变量,都称之为广义上的对象,例如数组、函数等。

  创建isObject工具函数,更多参考 lodash.isObject。

function isObject(value) {const type = typeof valuereturn value !== null && (type === 'object' || type === 'function')
}

  然后阅读规范「Promise/A+ 2.3.3」小节,省略部分暂时不考虑。

  • 如果x是一个对象或函数(广义对象)
    • thenx.then
    • 如果获取x.then导致抛出了一个异常e,用e作为原因拒绝promise
    • 如果then是一个函数,用x作为this调用它,且包含两个参数,分别为resolvePromiserejectPromise
      • 如果resolvePromise用一个值y调用,运行[[Resolve]](promise, y)
      • 如果rejectPromise用一个原因r调用,用r拒绝promise
      • ...
      • 如果调用then抛出了一个异常e
        • ...
        • 否则用e作为作为原因拒绝promise
    • 如果then不是一个函数,用x解决promise

  转译为代码。

function resolvePromise(promise, x, resolve, reject) {...if (x instanceof Promise) {...} else {if (isObject(x)) {var thentry {then = x.then} catch (e) {reject(e)}if (typeof then === 'function') {try {then.call(x,y => {resolvePromise(promise, y, resolve, reject)},r => {reject(r)})} catch (e) {reject(e)}} else {resolve(x)}} else {resolve(x)}}
}

  规范中运行[[Resolve]](promise, y),即递归resolvePromise,为什么呢?

  原因在于y值可能还是promise或者广义对象等等。

  我们来看一个原生Promise示例。

const p = new Promise(resolve => {resolve()
})
const thenable1 = {then(reslove) {reslove(1)},
}
const thenable2 = {then(resolve) {resolve(thenable1)},
}p.then(() => {return thenable2
})
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 1,
// }

优先级

  以下为刚才省略的部分。

  • 如果then是一个函数...
    • ...
    • ...
    • 如果resolvePromiserejectPromise都被调用,或者对其中一个多次调用,那么第一次调用优先,以后的调用都会被忽略
    • 如果调用then抛出了...
      • 如果resolvePromiserejectPromise已经被调用,则忽略它
      • ...

  为了限制哪种情况呢?

  还是来看一个原生Promise示例。

const p = new Promise(resolve => {resolve()
})
const thenable1 = {then(reslove) {setTimeout(() => {reslove(2)}, 0)},
}
const thenable2 = {then(resolve) {resolve(thenable1)resolve(1)},
}p.then(() => {return thenable2
})
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 2,
// }

  代码如何优化呢?

  我们可定义布尔变量called,标记是否运行参数resolvePromiserejectPromise。然后在第一次运行时将called修改为true,而以后的都会return被忽略。

if (typeof then === 'function') {var called = falsetry {then.call(x,y => {if (called) returncalled = trueresolvePromise(promise, y, resolve, reject)},r => {if (called) returncalled = truereject(r)})} catch (e) {if (called) returncalled = truereject(e)}
}

thenable

  规范「Promise/A+ 1.1」小结陈述了。

  promise是一个对象或函数(广义对象),存在then方法且行为符合规范。

  第三方Promise库、原生Promise以及我们手写版本Promise,创建的promise实例,其实都是标准的promise类型。

  而代码中x instanceof Promise语句,检验是否为promise类型,就有问题了。例如x被第三方库创建,也是标准promise类型,但是并不会运行if语句体,而是错误地运行else语句体。

function resolvePromise(promise, x, resolve, reject) {...if (x instanceof Promise) {...} else {...}
}

  还有方法可确定xpromise类型吗?答案是没有。

  怎么办呢?

  既然无法检验promise类型,那就退而求其次,检验类似promise类型的,即鸭式辩型。

鸭子类型(duck typing),也叫鸭式辩型,一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子

  规范「Promise/A+ 1.2」提出了thenable类型,即定义了then方法的对象或函数。

{then() {...},
}

thenablepromise的鸭子类型

  检验是否为promise类型,则降级为检验是否为thenable类型。

  代码修改为。

function resolvePromise(promise, x, resolve, reject) {if (promise === x) {return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (isObject(x)) {var thentry {then = x.then} catch (e) {reject(e)}if (typeof then === 'function') {var called = falsetry {then.call(x,y => {if (called) returncalled = trueresolvePromise(promise, y, resolve, reject)},r => {if (called) returncalled = truereject(r)})} catch (e) {if (called) returncalled = truereject(e)}} else {resolve(x)}} else {resolve(x)}
}

测试

  安装官方测试用例 promises-aplus-tests。

npm i promises-aplus-tests -D

  promise代码中新增以下。

// promise.js
class Promise {...
}Promise.deferred = () => {const result = {}result.promise = new Promise((resolve, reject) => {result.resolve = resolveresult.reject = reject})return result
}module.exports = Promise

  新增测试命令。

// package.json
{..."scripts": {"test": "promises-aplus-tests promise.js"},..."devDependencies": {"promises-aplus-tests": "^2.1.2"}
}

  运行npm run test

在这里插入图片描述

小结

  全文共计两万五千字有余,参考Promise/A+规范手写了then方法和promise解决程序。

  相关代码可参考 promise-coding 仓库,支持node和浏览器环境测试。

  如何手写Promise到此就结束了。

扩展

  学有余力或意犹未尽的伙伴们。

  贴出两个代码片段,可在原生Promise与手写Promise环境下运行。

// 1
new Promise(resolve => {resolve(Promise.resolve())
}).then(() => {console.log(3)
})Promise.resolve().then(() => {console.log(1)}).then(() => {console.log(2)}).then(() => {console.log(4)})// 2
Promise.resolve().then(() => {console.log(0)return Promise.resolve()}).then(() => {console.log(4)})Promise.resolve().then(() => {console.log(1)}).then(() => {console.log(2)}).then(() => {console.log(3)}).then(() => {console.log(5)}).then(() => {console.log(6)})

  看看能否分析出两者之间的细微差异?答案是不能。

  更多请持续关注更文,或在参考链接中探索一二。

参考

  • Promise/A+ 规范译文
  • 原生 Promise 和手写 Promise 的区别是什么?
  • resolve 时序
  • V8 源码解读 Promise

🎉 写在最后

🍻伙伴们,如果你已经看到了这里,觉得这篇文章有帮助到你的话不妨点赞👍或 Star ✨支持一下哦!

手动码字,如有错误,欢迎在评论区指正💬~

你的支持就是我更新的最大动力💪~

GitHub / Gitee、GitHub Pages、掘金、CSDN 同步更新,欢迎关注😉~

http://www.dinnco.com/news/27086.html

相关文章:

  • 最全的游戏网站天津seo排名
  • 谷歌seo网站推广百度推广服务费一年多少钱
  • wordpress 网站加密seo网站快速排名外包
  • 最权威的做网站的公司哪家好电商seo
  • 高端营销型网站制作关键词优化顾问
  • 做卖东西的网站自己开网站怎么开
  • 网站建设日程表模板深圳网站建设优化
  • 一般做网站用什么字体收录提交入口网址
  • 网站建设加盟模式一般网络推广应该怎么做
  • wordpress怎么切换编辑器廊坊优化外包
  • nas 做网站服务器114黄页
  • 国内大型免费网站建设关键词排名工具有哪些
  • 做丰胸网站网络营销课程去哪里学
  • 网站上飘窗怎么做推广网站的文案
  • wamp做的网站上传seo做得比较好的公司
  • 商城网站建设怎么样百度客服中心人工电话
  • 辽宁人社app一直更新超级优化大师
  • 上海网站公安备案号2021国内最好用免费建站系统
  • 美团网网站建设 费用seo免费工具
  • 网站小游戏怎么做公司seo是什么级别
  • 软件开发项目甘特图seo好找工作吗
  • 网站验收标准百度竞价推广流程
  • 手机可以创建网站吗semir是什么意思
  • wordpress增加开场动画技术优化seo
  • 长宁网站建设公司专门做网站的公司
  • 可以做营销任务的网站百度手机助手下载安装
  • 西安网站建设制作熊掌号企业培训
  • 制作网页小图片关键词优化搜索排名
  • 做游戏都需要什么网站吗百度网址是多少 百度知道
  • 五 网站开发总体进度安排手机seo快速排名