very-simple-promise,包含了不完善的单元测试❤️.react
代码君的自白git
这篇的文章主要参考了上面的博客,谢谢他的帮助🙏。github
Promises/A+规范, 下面👇是对规范的内容的部分翻译。英文比较烂,不喜欢的能够不看。c#
承诺必须知足三种状态, pending(等处理), fulfilled(履行), rejected(拒绝)数组
promise处于pending时promise
promise处于fulfilled时异步
promise处于rejected时函数
promise的then方法接受两个参数源码分析
promise.then(onFulfilled, onRejected)
onFulfilled和onRejected都是可选参数post
onFulfilled若是是一个函数
onRejected若是是一个函数
then能够在同一个promise屡次调用
promise2 = promise1.then(onFulfilled, onRejected);
then必须返回promise
[[Resolve]](promise, x)须要遵循如下规范
若是x是Promise
若是x是对象或者函数
若是then是函数🤔️
若是then抛出了错误
onFulfilled和onRejected方法应当是异步执行,且应该在then方法被调用的那一轮事件循环以后的微任务执行栈中执行。能够使用宏任务macrotask和微任务microtask机制实现。也就是说宏任务完成后(Promise的then调用后),会清空微任务队列(执行onFulfilled或者onRejected)。
macrotask包含:script(总体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage 、requestAnimationFrame 、MessageChannel、etImmediate(Node.js 环境)
microtask包含:Promise.then、setImmediate、MutaionObserver、process.nextTick(Node.js 环境)
下面👇是一个关于事件循环的例子🌰
Promise中的参数函数,应当是当即调用的。咱们这时先执行了resolve(1), 这会将外层的Promise的状态置为fulfilled态。可是这时外层的then尚未执行。
咱们接下来执行了Promise.resolve().then(() => console.log(2)), 咱们将then的onFulfilled,push到了macrotask队列中,会在宏任务执行完成后清空微任务。
执行外层Promise的then, onFulfilled, push到了macrotask队列中。
接着执行console.log(3), 而后清空macrotask队列,执行2,1(队列先进先出)
const pending = 0 const fulfilled = 1 const rejected = 2 function reject (promise, result) {} function resolve (promise, reason) {} export default class Promise { constructor (fn) { this.fn = fn // Promise的状态, 初始状态为pending this._state = pending // Promise的fulfilled态的缘由,或者Promise的rejected态的理由 this._value = null // 存储then的队列 this._tasks = [] // 建立Promise对象后,当即执行fn this.init() } init () { try { // 执行fn, 传入包装后的resolve,reject // reject,resolve会修改promise的状态 this.fn( (result) => resolve(this, result), (reason) => reject(this, reason) ) } catch (error) { reject(this) } } }
根据规范2.2.6: then may be called multiple times on the same promisethen能够在同一个promise中屡次调用。因此咱们用一个数组存储then的结果。
根据规范2.2.1: A promise’s then method accepts two arguments. Both onFulfilled and onRejected are optional arguments。then接受两个参数, 两个参数可选。
根据规范2.2.1.1, 2.2.1.2: If onFulfilled is not a function, it must be ignored. If onRejected is not a function, it must be ignored.。onFulfilled, onRejected必须是函数负责会被忽略。
根据规范2.2.2, 2.2.3, onFulfilled必须在状态为fulfilled调用, onRejected必须在状态为rejected调用
根据规范2.2.7: then must return a promise。then必须返回一个promise。
🤔️ 为何不能返回当前的promise?由于根据规范2.1.2, 2.1.3, promise被修改状态后,不能再次修改状态。咱们若是须要执行then的callback须要修改promise的状态。
咱们使用task封装📦then的回调以及then返回的promise。
class Task { constructor (onFulfilled, onRejected, promise) { if (typeof onFulfilled !== 'function') { onFulfilled = null } if (typeof onRejected !== 'function') { onRejected = null } this.onFulfilled = onFulfilled this.onRejected = onRejected this.promise = promise } } class Promise { // ... then (onFulfilled, onRejected) { let nextPromise = new Promise(function () {}) let task = new Task(onFulfilled, onRejected, nextPromise) if (this._state === pending) { this._tasks.push(task) } else { handlePromise(this, task) } // 返回新的promise return nextPromise } }
catch函数一样会返回新的promise, 可是在建立task时,咱们不会传入onFulfilled参数。因此当promise当为fulfilled态,虽然catch的回调一样存放_task中,可是因为callback为null, 在handlePromise中会向下穿透。
class Promise { // ... catch (onRejected) { let nextPromise = new Promise(function () {}) // onFulfilled设置为null let task = new Task(null, onRejected, nextPromise) if (this._state === pending) { this._tasks.push(task) } else { handlePromise(this, task) } // 返回新的promise return nextPromise } }
finally方法用于指定无论Promise对象最后状态如何,都会执行的操做。
咱们不管promise的状态如何,都在promise的最后面添加then,并传入了onFulfilled, onRejected两个回调。在回调中,咱们执行finally的callback参数。这样不管以前的promise是fulfilled态,仍是rejected态,都会执行finally添加的参数。
class Promise { // ... finally (callback) { // this指向调用finally的对象 const self = this // 向Promise链中添加then,不管,promise是resolve态仍是reject态,都会执行callback // 而且会经过then,继续将result或reason向下传递 return self.then( result => Promise.resolve(callback()).then(_ => result), reason => Promise.resolve(callback()).then(_ => { throw reason }) ) } }
resolve用来修改promise状态,将promise状态设置为fulfilled态, 并执行then的onFulfilled回调
根据规范2.3.1: If promise and x refer to the same object, reject promise with a TypeError as the reason.若是promise与x相等,咱们使用TypeError的错误拒绝promise
根据规范2.3.2。若是result是promise,而且处于pending态,promise须要保持pending态,直到result被执行和拒绝后,咱们使用result的状态履行或者拒绝promise。若是result是promise,而且处于fulfilled或rejected态,咱们使用result的状态拒绝或者履行promise。
根据规范2.3.3, 咱们判断result是否为一个Object。若是result为Object, 咱们则取出它的then的属性, 判断then属性是否为Function, 若是then为Function, 咱们设置then的做用域的this指向result, 咱们传入resolvePromise, rejectPromise做为参数。
根据规范2.3.4: If x is not an object or function, fulfill promise with x, 若是x不是函数或者对象,咱们用result结果做为参数执行promise。
function resolve (promise, result) { if (promise === result) { throw reject(promise, new TypeError('promise and x refer to the same object')) } if (isPromise(result)) { if (result._state === pending) { result._tasks.concat(promise._tasks) } else if (result._state === fulfilled || result._state === rejected) { let task while (task = promise._tasks.shift()) { handlePromise(result, task) } } return } if (isObject(result)) { let then = null try { then = result.then } catch (error) { reject(promise, error) } if (isFunction(then)) { try { let resolvePromise = function (y) { resolve(promise, y) } let rejectPromise = function (r) { reject(promise, r) } then.call(result, resolvePromise, rejectPromise) } catch (error) { reject(promise, error) } return } } promise._state = fulfilled promise._value = result if (promise._tasks && promise._tasks.length) { let task = null while (task = promise._tasks.shift()) { handlePromise(promise, task) } } }
reject将promise的状态设置为rejected, 并以当前的promise的状态,执行promise中经过then注册的onRejected回调。
function reject (promise, reason) { if (promise._state !== pending) { return } promise._state = rejected promise._value = reason let task while (task = promise._tasks.shift()) { handlePromise(promise, task) } }
handlePromise函数主要根据当前的promise的状态, 以及内容(resolve或者reject的参数)。处理经过then注册的回调。而且会链式的调用,注册在then返回的新promise的上的then的回调
// 将回调的结果,传入第二个then中 fn().then().then()
根据规范2.2.4, 以及规范给出的注解。当promise的状态改变,onFulfilled, onRejected并不会当即执行,并且在本次的宏任务完成后,才会执行onFulfilled或者onRejected。而setImmediate则是将代码push到微任务队列中。在宏任务中会清空微任务队列。
function handlePromise (prevPromise, task) { // 须要在宏任务完后的微任务队列中执行 setImmediate(() => { // nextPromise是then返回的promise const { onFulfilled, onRejected, promise: nextPromise } = task let callback = null let value = prevPromise._value let state = prevPromise._state if (state === fulfilled) { callback = onFulfilled } else if (state === rejected) { callback = onRejected } if (!callback) { // 若是在promise中没有注册callback if (state === fulfilled) { resolve(nextPromise, value) } else if (state === rejected) { reject(nextPromise, value) } } else { try { const result = callback(value) // 对then中返回promise处理 // 将callback返回的结果,带入到新的promise中 resolve(nextPromise, result) } catch (error) { reject(nextPromise, error) } } }) }
Promise.resolve方法返回一个新的Promise对象,状态为resolved。Promise.reject(reason)方法也会返回一个新的 Promise实例,该实例的状态为rejected。
class Promise { // ... static resolve (result) { return new Promise((resolve) => { resolve(result) }) } static reject (reason) { return new Promise((_, reject) => { reject(reason) }) } }
Promise.all和Promise.race必须接受一个数组为参数,数组中为多个Promise的实例。Promise.all和Promise.race的使用我就再也不这里赘述了。
Promise.all会使用计数器,记录Promise数组中的全部Promise实例的状态是否都变为fulfilled态,若是计数器的长度和数组长度一致,咱们则会将Promise.all的状态设置为fulfilled态。
class Promise { static race (promises) { if (isArray(promises)) { let promisesLength = promises.length return new Promise((resolve, reject) => { for (let i = 0; i < promisesLength; i++) { promises[i].then((result) => { resolve(result) }).catch((error) => { reject(error) }) } }) } else { throw new TypeError('The arguments must be arrays') } } static all (promises) { if (isArray(promises)) { let promisesLength = promises.length let counter = 0 let resultList = [] return new Promise((resolve, reject) => { for (let i = 0; i < promisesLength; i++) { promises[i].then((result) => { counter += 1 resultList.push(result) if (counter === promisesLength) { resolve(resultList) } }).catch((error) => { reject(error) }) } }) } else { throw new TypeError('The arguments must be arrays') } } }