promise
手写promise --核心功能-->方法-->promise/A+
参考
mdn--promise
.
.
promise中的核心功能
构造函数
/** * 构造函数 * 1. 定义类 * 2. 添加构造函数 * 3. 定义resolve/reject * 4. 执行回调函数 */ class myPromise { constructor(func) { const resolve = (result) => { console.log('resolve 执行: ' + result); } const reject = (result) => { console.log('reject 执行: ' + result); } func(resolve, reject) } } // 测试用例 const p = new myPromise((resolve, reject) => { resolve('success') // reject('error') })
.
状态及原因
一个 Promise 必然处于以下几种状态之一:
待定(pending):初始状态,既没有被兑现,也没有被拒绝。
已兑现(fulfilled):意味着操作成功完成。
已拒绝(rejected):意味着操作失败。
一个待定的 Promise 最终状态可以是已兑现并返回一个值,或者是已拒绝并返回一个原因(错误)。当其中任意一种情况发生时,通过 Promise 的 then 方法串联的处理程序将被调用。如果绑定相应处理程序时 Promise 已经兑现或拒绝,这处理程序将被立即调用,因此在异步操作完成和绑定处理程序之间不存在竞态条件。
如果一个 Promise 已经被兑现或拒绝,即不再处于待定状态,那么则称之为已敲定(settled)。即状态不可逆。
.
const PENDING = 'pending' const FULFILLED = 'fullfilled' const REJECTED = 'reject' class myPromise { // 1. 添加状态 state = PENDING // 2. 添加原因 result = undefined constructor(func) { const resolve = (result) => { // 3. 调整状态 // 4. 添加原因 if (this.state === PENDING) { this.state = FULFILLED this.result = result } } const reject = (result) => { // 3. 调整状态 // 4. 添加原因 if (this.state === PENDING) { this.state = REJECTED this.result = result } } func(resolve, reject) } } // 测试用例 const p = new myPromise((resolve, reject) => { resolve('success') reject('error') // 状态已敲定为fulfilled, 即便执行reject也不会改变状态了 })
.
then方法
> 成功和失败回调
onFulfilled
可选
一个在此 Promise 对象被兑现时异步执行的函数。它的返回值将成为 then() 返回的 Promise 对象的兑现值。此函数被调用时将传入以下参数:
value
Promise 对象的兑现值。
如果 onFulfilled
不是一个函数,则内部会被替换为一个恒等函数((x) => x)
,它只是简单地将兑现值向前传递。
onRejected
可选
一个在此 Promise 对象被拒绝时异步执行的函数。它的返回值将成为 catch() 返回的 Promise 对象的兑现值。此函数被调用时将传入以下参数:
reason
Promise 对象被拒绝的原因。
如果 onRejected
不是一个函数,则内部会被替换为一个抛出器函数((x) => { throw x; })
,它会抛出它收到的拒绝原因。
const PENDING = 'pending' const FULFILLED = 'fullfilled' const REJECTED = 'reject' class myPromise { state = PENDING result = undefined constructor(func) { const resolve = (result) => { if (this.state === PENDING) { this.state = FULFILLED this.result = result } } const reject = (result) => { if (this.state === PENDING) { this.state = REJECTED this.result = result } } func(resolve, reject) } // 1. 添加实例方法 then(onFulfilled, onRejected) { // 2. 参数判断 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x onRejected = typeof onRejected === 'function' ? onRejected : x => { throw x } // 3. 分情况执行回调 if (this.state === FULFILLED) { onFulfilled(this.result) } else if (this.state === REJECTED) { onRejected(this.result) } } } // 测试用例 const p = new myPromise((resolve, reject) => { // resolve('success') reject('error') }) p.then(res => { console.log("回调成功: ", res); }, err => { console.log("回调失败: ", err); })
> 异步和多次调用
const PENDING = 'pending' const FULFILLED = 'fullfilled' const REJECTED = 'reject' class myPromise { state = PENDING result = undefined // 1. 定义实例属性, 保存传入的回调函数, 用对象数组来存 #handlers = [] constructor(func) { const resolve = (result) => { if (this.state === PENDING) { this.state = FULFILLED this.result = result // 3. 调用成功回调 this.#handlers.forEach(({ onFulfilled }) => { onFulfilled(this.result) }) } } const reject = (result) => { if (this.state === PENDING) { this.state = REJECTED this.result = result // 4. 调用失败回调 this.#handlers.forEach(({ onRejected }) => { onRejected(this.result) }) } } func(resolve, reject) } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x onRejected = typeof onRejected === 'function' ? onRejected : x => { throw x } if (this.state === FULFILLED) { onFulfilled(this.result) } else if (this.state === REJECTED) { onRejected(this.result) } else if (this.state === PENDING) { // 2. 保存回调函数 this.#handlers.push({ onFulfilled, onRejected }) } } } // 测试用例 const p = new myPromise((resolve, reject) => { setTimeout(() => { resolve('success') reject('error') }, 2000) }) p.then(res => { console.log("then1: ", res); }, err => { console.log("then1: ", err); }) p.then(res => { console.log("then2: ", res); }, err => { console.log("then2: ", err); })
.
异步任务
> 核心api
为了实现异步任务, vue2使用的核心api有: Promise.then、MutationObserver 、 setImmediate 、 setTimeout
这里我们选用: queueMicrotask 、MutationObserver 、 setTimeout
使用 queueMicrotask 注册的回调函数会在当前宏任务的所有同步代码执行完毕后,但在下一个宏任务(如 DOM 更新、渲染等)开始之前执行。
MutationObserver 接口提供了一种异步监视 DOM 变动的能力。当 DOM 发生变化时,MutationObserver 会触发回调函数。
setTimeout 函数用于设置一个定时器,该定时器在指定的延迟时间后执行一个函数或指定的代码片段。
开启异步任务:
// 1. 异步任务 ==> queueMicrotask console.log(1); queueMicrotask(() => { console.log('queueMicrotask'); }) console.log('2'); // 2. 异步任务 ==> MutationObeserver console.log(1); // 创建并返回一个新的观察器,它会在触发指定 DOM 事件时,调用指定的回调函数 const mo = new MutationObserver(() => { console.log('mutationObserver'); }) // 创建 const divDom = document.createElement('div') // 监听子节点改变 mo.observe(divDom, { childList: true }) // 修改内容触发回调函数 divDom.innerText = 'hehe' console.log('2'); // 3. 异步任务 ==> setTimeout console.log(1); setTimeout(() => { console.log('setTimeout'); }, 0) console.log(2);
.
> 函数封装
then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x onRejected = typeof onRejected === 'function' ? onRejected : x => { throw x } // 3. 使用封装函数 if (this.state === FULFILLED) { myAsync(() => { onFulfilled(this.result) }) } else if (this.state === REJECTED) { myAsync(() => { onRejected(this.result) }) } else if (this.state === PENDING) { this.#handlers.push({ onFulfilled: () => { myAsync(() => { onFulfilled(this.result) }) }, onRejected: () => { myAsync(() => { onRejected(this.result) }) } }) } } } // 1. 定义函数 function myAsync(callback) { // 2. 调用核心api if (typeof queueMicrotask === 'function') { queueMicrotask(callback) } else if (typeof MutationObserver === 'function') { const mo = new MutationObserver(callback) const divDom = document.createElement('div') mo.observe(divDom, { childList: true }) divDom.innerText = 'hehe' } else { setTimeout(callback, 0) } } // 测试用例 console.log('top'); const p = new myPromise((resolve, reject) => { resolve('success') reject('error') }) p.then(res => { console.log("then: ", res); }, err => { console.log("then: ", err); }) console.log('bottom');
.
.
.
链式编程
由于这些方法返回 Promise,因此它们可以被链式调用。
.
FULFILLED状态下处理返回值和异常
then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x onRejected = typeof onRejected === 'function' ? onRejected : x => { throw x } // 1. 返回新的promise实例 const p2 = new myPromise((resolve, reject) => { if (this.state === FULFILLED) { myAsync(() => { try { // 2. 获取返回值 const x = onFulfilled(this.result) // console.log(x); // 3. 处理返回值 resolve(x) } catch (error) { reject(error) } }) } else if (this.state === REJECTED) { myAsync(() => { onRejected(this.result) }) } else if (this.state === PENDING) { this.#handlers.push({ onFulfilled: () => { myAsync(() => { onFulfilled(this.result) }) }, onRejected: () => { myAsync(() => { onRejected(this.result) }) } }) } }) return p2 } } // 测试用例 const p = new myPromise((resolve, reject) => { resolve('1') // reject('error') }) p.then(res => { console.log("p1: ", res); throw 'throw error' return 2 }).then(res => { console.log("p2: ", res) }, err => { console.log("p2: ", err); })
.
.
FULFILLED状态下处理返回promise
myAsync(() => { try { const x = onFulfilled(this.result) // 1. 处理返回的promise if (x instanceof myPromise) { // 2. 调用then方法 x.then(res => resolve(res), err => reject(err)) } else { resolve(x) } } catch (error) { reject(error) } } // 测试用例 const p = new myPromise((resolve, reject) => { resolve('1') }) p.then(res => { return new myPromise((resolve, reject) => { // resolve(2) reject('error') }) }).then(res => { console.log("p2: ", res) }, err => { console.log("p2: ", err); })
.
.
FULFILLED状态下处理重复引用
try { const x = onFulfilled(this.result) // 1. 处理重复的引用 if (x === p2) { // 2. 抛出错误 throw new TypeError('chaining cycle detected for promise #<#Promise>') } if (x instanceof myPromise) { x.then(res => resolve(res), err => reject(err)) } else { resolve(x) } } // 测试用例 const p = new myPromise((resolve, reject) => { resolve(1) }) const p2 = p.then(res => { return p2 }) p2.then( res => { }, err => console.log('err', err) )
.
.
REJECTED状态
else if (this.state === REJECTED) { myAsync(() => { // 1. 处理异常 try { // 2. 获取返回值 const x = onRejected(this.result) // 4. 调用函数 resovlePromise(p2, x, resolve, reject) } catch (error) { reject(error) } }) } // 3. 抽取函数 function resovlePromise(p2, x, resolve, reject) { if (x === p2) { throw new TypeError('chaining cycle detected for promise #<#Promise>') } if (x instanceof myPromise) { x.then(res => resolve(res), err => reject(err)) } else { resolve(x) } } // 测试用例 const p = new myPromise((resolve, reject) => { reject(1) }) const p2 = p.then(undefined, err => { // throw 'error' // return p2 // return 2 return new myPromise((resolve, reject) => { resolve('myPromise-2') }) }) p2.then( res => { console.log('p2-res', res); }, err => console.log('p2-err', err) )
.
.
PENDING状态
else if (this.state === PENDING) { this.#handlers.push({ onFulfilled: () => { myAsync(() => { try { const x = onFulfilled(this.result) resovlePromise(p2,x,resolve,reject) } catch (error) { reject(error) } }) }, onRejected: () => { myAsync(() => { try { const x = onRejected(this.result) resovlePromise(p2,x,resolve,reject) } catch (error) { reject(error) } }) } }) } } // 测试用例 const p = new myPromise((resolve, reject) => { setTimeout(() => { resolve(1) }, 2000) }) const p2 = p.then(res => { throw 'error' // return p2 // return 2 // return new myPromise((resolve, reject) => { // resolve('resolve-2') // reject('reject-2') // }) }) p2.then( res => { console.log('p2-res', res); }, err => console.log('p2-err', err) )
.
.
.
promise中的实例方法
.catch()方法
Promise.prototype.catch()
将一个拒绝处理回调函数附加到 Promise 上,并返回一个新的 Promise,如果回调被调用,则解决为回调的返回值,如果 Promise 被兑现,解决为其原始兑现值。
Promise
实例的catch()
方法用于注册一个在promise
被拒绝时调用的函数。它会立即返回一个等效的Promise
对象,这可以允许你链式调用其他promise
的方法。此方法是Promise.prototype.then(undefined, onRejected)
的一种简写形式。
Promise.prototype.finally()
将一个处理器附加到 Promise 上,并返回一个新的 Promise,当原始 Promise 被解决时解决。无论 Promise 是否被兑现还是被拒绝,处理器都会在 Promise 敲定时被调用。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战