Promise原理浅析以及简易实现
一、什么是Promise
Promise 是一种用于异步编程的设计模式,它解决了传统回调函数(callback)所带来的回调地狱(Callback Hell)问题。在 JavaScript 中,Promise 是 ECMAScript 6(ES6)引入的一种特性,用于处理异步操作,使得异步代码更易于阅读、编写和维护。
二、Promise 原理浅析
- **对象状态(State):** Promise 有三种状态:Pending(进行中)、Fulfilled(已成功)和Rejected(已失败)。初始状态是 Pending,异步操作完成时,Promise 可以由 Pending 转为 Fulfilled,表示操作成功;或者由 Pending 转为 Rejected,表示操作失败。状态一旦转变,就不可再改变。
- **then 方法:** Promise 对象上有一个 then 方法,用于指定异步操作成功或失败后要执行的回调函数。then 方法接收两个参数,第一个参数是处理成功情况的回调函数(onFulfilled),第二个参数是处理失败情况的回调函数(onRejected)。
- **回调队列:** 当 Promise 处于 Pending 状态时,then 方法注册的回调函数不会立即执行。相反,它们会被添加到一个回调队列中,等待 Promise 的状态改变。
- **状态转移和回调触发:** 当 Promise 状态由 Pending 转为 Fulfilled 或 Rejected 时,将会按照注册的回调函数的顺序执行对应的处理函数。如果是 Fulfilled,执行 onFulfilled 回调;如果是 Rejected,执行 onRejected 回调。这些回调函数可以访问到 Promise 返回的值或错误信息。
- **错误处理:** 如果在回调函数执行过程中发生异常(异常被捕获),Promise 将转为 Rejected 状态,后续的错误处理回调(then 的第二个参数或后续 catch 方法)将会被触发。
- **链式调用:** then 方法返回的仍然是一个 Promise 对象,因此可以进行链式调用。这样可以更方便地处理多个异步操作的依赖关系。
三、简易实现
// Promise异步实现 // 先定义三个常量表示状态 const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; // 新建 MyPromise 类 class MyPromise { constructor(executor) { // executor 是一个执行器,进入会立即执行 // 并传入resolve和reject方法 executor(this.resolve, this.reject) } // 储存状态的变量,初始值是 pending status = PENDING; // resolve和reject为什么要用箭头函数? // 如果直接调用的话,普通函数this指向的是window或者undefined // 用箭头函数就可以让this指向当前实例对象 // 成功之后的值 value = null; // 失败之后的原因 reason = null; // 存储成功回调 onFulfilledCallback = null; // 存储失败回调 onRejectedCallback = null; // 更改成功后的状态 resolve = (value) => { // 只有状态是等待,才执行状态修改 if (this.status === PENDING) { // 状态修改为成功 this.status = FULFILLED; // 保存成功之后的值 this.value = value; // 成功回调存在调用 this.onFulfilledCallback && this.onFulfilledCallback(value) } } // 更改失败后的状态 reject = (reason) => { // 只有状态是等待,才执行状态修改 if (this.status === PENDING) { // 状态成功为失败 this.status = REJECTED; // 保存失败后的原因 this.reason = reason; // 失败回调存在调用 this.onRejectedCallback && this.onRejectedCallback(reason) } } then(onFulfilled, onRejected) { // 判断状态 if (this.status === FULFILLED) { // 调用成功回调,并且把值返回 onFulfilled(this.value); } else if (this.status === REJECTED) { // 调用失败回调,并且把原因返回 onRejected(this.reason); } else if(this.status === PENDING){ // 不知道后面状态变化,故存储成功和失败的回调;待执行成功失败回调再传递 this.onFulfilledCallback = onFulfilled; this.onRejectedCallback = onRejected; } } } module.exports = MyPromise 异步实现版: class myPromise { // 为了统一用static创建静态属性,用来管理状态 static PENDING = 'pending'; static FULFILLED = 'fulfilled'; static REJECTED = 'rejected'; // 构造函数:通过new命令生成对象实例时,自动调用类的构造函数 constructor(func) { // 给类的构造方法constructor添加一个参数func this.PromiseState = myPromise.PENDING; // 指定Promise对象的状态属性 PromiseState,初始值为pending this.PromiseResult = null; // 指定Promise对象的结果 PromiseResult this.onFulfilledCallbacks = []; // 保存成功回调 this.onRejectedCallbacks = []; // 保存失败回调 try { /** * func()传入resolve和reject, * resolve()和reject()方法在外部调用,这里需要用bind修正一下this指向 * new 对象实例时,自动执行func() */ func(this.resolve.bind(this), this.reject.bind(this)); } catch (error) { // 生成实例时(执行resolve和reject),如果报错,就把错误信息传入给reject()方法,并且直接执行reject()方法 this.reject(error) } } resolve(result) { // result为成功态时接收的终值 // 只能由pedning状态 => fulfilled状态 (避免调用多次resolve reject) if (this.PromiseState === myPromise.PENDING) { /** * 为什么resolve和reject要加setTimeout? * 2.2.4规范 onFulfilled 和 onRejected 只允许在 execution context 栈仅包含平台代码时运行. * 注1 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。 * 这个事件队列可以采用“宏任务(macro-task)”机制,比如setTimeout 或者 setImmediate; 也可以采用“微任务(micro-task)”机制来实现, 比如 MutationObserver 或者process.nextTick。 */ setTimeout(() => { this.PromiseState = myPromise.FULFILLED; this.PromiseResult = result; /** * 在执行resolve或者reject的时候,遍历自身的callbacks数组, * 看看数组里面有没有then那边 保留 过来的 待执行函数, * 然后逐个执行数组里面的函数,执行的时候会传入相应的参数 */ this.onFulfilledCallbacks.forEach(callback => { callback(result) }) }); } } reject(reason) { // reason为拒绝态时接收的终值 // 只能由pedning状态 => rejected状态 (避免调用多次resolve reject) if (this.PromiseState === myPromise.PENDING) { setTimeout(() => { this.PromiseState = myPromise.REJECTED; this.PromiseResult = reason; this.onRejectedCallbacks.forEach(callback => { callback(reason) }) }); } } /** * [注册fulfilled状态/rejected状态对应的回调函数] * @param {function} onFulfilled fulfilled状态时 执行的函数 * @param {function} onRejected rejected状态时 执行的函数 * @returns {function} newPromsie 返回一个新的promise对象 */ then(onFulfilled, onRejected) { /** * 参数校验:Promise规定then方法里面的两个参数如果不是函数的话就要被忽略 * 所谓“忽略”并不是什么都不干, * 对于onFulfilled来说“忽略”就是将value原封不动的返回, * 对于onRejected来说就是返回reason, * onRejected因为是错误分支,我们返回reason应该throw一个Error */ onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; }; // 2.2.7规范 then 方法必须返回一个 promise 对象 let promise2 = new myPromise((resolve, reject) => { if (this.PromiseState === myPromise.FULFILLED) { /** * 为什么这里要加定时器setTimeout? * 2.2.4规范 onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用 注1 * 这里的平台代码指的是引擎、环境以及 promise 的实施代码。 * 实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。 * 这个事件队列可以采用“宏任务(macro-task)”机制,比如setTimeout 或者 setImmediate; 也可以采用“微任务(micro-task)”机制来实现, 比如 MutationObserver 或者process.nextTick。 */ setTimeout(() => { try { // 2.2.7.1规范 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x),即运行resolvePromise() let x = onFulfilled(this.PromiseResult); resolvePromise(promise2, x, resolve, reject); } catch (e) { // 2.2.7.2 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e reject(e); // 捕获前面onFulfilled中抛出的异常 } }); } else if (this.PromiseState === myPromise.REJECTED) { setTimeout(() => { try { let x = onRejected(this.PromiseResult); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }); } else if (this.PromiseState === myPromise.PENDING) { // pending 状态保存的 resolve() 和 reject() 回调也要符合 2.2.7.1 和 2.2.7.2 规范 this.onFulfilledCallbacks.push(() => { try { let x = onFulfilled(this.PromiseResult); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); } }); this.onRejectedCallbacks.push(() => { try { let x = onRejected(this.PromiseResult); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); } }) return promise2 } } /** * 对resolve()、reject() 进行改造增强 针对resolve()和reject()中不同值情况 进行处理 * @param {promise} promise2 promise1.then方法返回的新的promise对象 * @param {[type]} x promise1中onFulfilled或onRejected的返回值 * @param {[type]} resolve promise2的resolve方法 * @param {[type]} reject promise2的reject方法 */ function resolvePromise(promise2, x, resolve, reject) { // 2.3.1规范 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise if (x === promise2) { return reject(new TypeError('Chaining cycle detected for promise')); } // 2.3.2规范 如果 x 为 Promise ,则使 promise2 接受 x 的状态 if (x instanceof myPromise) { if (x.PromiseState === myPromise.PENDING) { /** * 2.3.2.1 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝 * 注意"直至 x 被执行或拒绝"这句话, * 这句话的意思是:x 被执行x,如果执行的时候拿到一个y,还要继续解析y */ x.then(y => { resolvePromise(promise2, y, resolve, reject) }, reject); } else if (x.PromiseState === myPromise.FULFILLED) { // 2.3.2.2 如果 x 处于执行态,用相同的值执行 promise resolve(x.PromiseResult); } else if (x.PromiseState === myPromise.REJECTED) { // 2.3.2.3 如果 x 处于拒绝态,用相同的据因拒绝 promise reject(x.PromiseResult); } } else if (x !== null && ((typeof x === 'object' || (typeof x === 'function')))) { // 2.3.3 如果 x 为对象或函数 try { // 2.3.3.1 把 x.then 赋值给 then var then = x.then; } catch (e) { // 2.3.3.2 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise return reject(e); } /** * 2.3.3.3 * 如果 then 是函数,将 x 作为函数的作用域 this 调用之。 * 传递两个回调函数作为参数, * 第一个参数叫做 `resolvePromise` ,第二个参数叫做 `rejectPromise` */ if (typeof then === 'function') { // 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用 let called = false; // 避免多次调用 try { then.call( x, // 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y) y => { if (called) return; called = true; resolvePromise(promise2, y, resolve, reject); }, // 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise r => { if (called) return; called = true; reject(r); } ) } catch (e) { /** * 2.3.3.3.4 如果调用 then 方法抛出了异常 e * 2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之 */ if (called) return; called = true; /** * 2.3.3.3.4.2 否则以 e 为据因拒绝 promise */ reject(e); } } else { // 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise resolve(x); } } else { // 2.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise return resolve(x); } }