-
1、promise状态基础知识点:
- 我们知道一个 promise 是有三个状态的,pending、fulfilled、rejected
- 当一个promise 被创建的时候,它的状态被初始化成了 pending 状态
- 同时 promise 提供了两个方法:resolve、reject 来修改这个promise状态;调用resolve方法将Promise的状态从pending变为fulfilled,而reject方法则将状态从pending变为rejected
-
2、现行promise常用使用态
- 现行的 promise 中,我们大多将 resolve、reject 放在 promise 内部来处理 promise 的状态
const promise = new Promise((resolve, reject) => { // 异步操作 if (/* 操作成功 */) { resolve(value); // 将 Promise 状态改为 fulfilled,并传递结果值 } else { reject(error); // 将 Promise 状态改为 rejected,并传递错误信息 } }).catch(err=>{console.log(err)}) -
3、现行promise常用使用态的一些使用思考
- 现行的使用常态,绝大部分场景都是能正常覆盖的
- 但是有时间我们希望,能在 promise 外部,自由控制调取 resolve、reject,来控制 promise 的状态变化,该怎么办呢?
-
4、我们的期许
- 在某些情况下,我们希望在创建 Promise 时不立即执行某些逻辑,而是在稍后的某个时间点控制何时执行和如何执行。这时,可以通过将执行逻辑封装在另一个函数中,然后在需要的时候调用这个函数来控制 resolve 和 reject 的调用。
- 将 Promise 的 resolve 和 reject 控制权从 Promise 构造函数中分离出来的编程模式。它创建了一个包含 Promise 及其解决方法的对象,允许你在 Promise 外部控制其状态
-
5、现有解法
-
我们可以封装,拆解出 promise deferred 【deferred译:延期 】 对象
-
deferred 对象通常包含三个部分:
- promise - 标准的 Promise 对象
- resolve - 用于解决 Promise 的函数
- reject - 用于拒绝 Promise 的函数
-
封装输出:
function getDeferredPromise() { let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; }-
与常态 Promise 的对比
特性 标准 Promise Deferred 模式的 Promise 控制权 在构造函数内部 外部可控制 解决时机 必须在构造函数中决定 可在任何时间、任何地点决定 适用场景 已知的异步操作 需要外部控制的异步操作
-
-
6、常用应用场景
-
1. 用户交互控制
function createConfirmationDialog() { const {promise, resolve, reject} = getDeferredPromise(); document.getElementById('confirm-btn').onclick = () => { resolve(true); }; document.getElementById('cancel-btn').onclick = () => { resolve(false); }; return promise; } // 使用 createConfirmationDialog().then(confirmed => { if (confirmed) { console.log('用户确认'); } else { console.log('用户取消'); } });-
2. 文件加载
function loadImg(){ const {promise, resolve, reject} = getDeferredPromise(); const Img = new Image() Img.src='xxx' Img.onload = function() { console.log('图片加载完成了,可以写业务了') resolve("图片加载成功了~~") }; Img.onerror = function() { console.log('图片加载完成了,可以写业务了') reject("图片加载失败了~~") }; return promise } loadImg().then(res=>{console.log("图片加载成功了~~")}).catch(err=> console.log(err, "图片加载失败了~~"))-
3. 异步操作队列
class AsyncQueue { constructor() { this.queue = []; this.current = null; } add(task) { const deferredPromise = getDeferredPromise(); this.queue.push({ task, deferredPromise }); if (!this.current) this.processNext(); return deferredPromise.promise; } processNext() { if (this.queue.length === 0) { this.current = null; return; } const { task, deferredPromise } = this.queue.shift(); this.current = task() .then(result => deferredPromise.resolve(result)) .catch(error => deferredPromise.reject(error)) .finally(() => this.processNext()); } }-
4. API 请求超时控制
function fetchWithTimeout(url, timeout) { const {promise, resolve, reject} = getDeferredPromise(); fetch(url) .then(response => resolve(response)) .catch(error => reject(error)); setTimeout(() => { reject(new Error('request timeout')); }, timeout); return promise; } -
-
7、 现代 JavaScript 中的替代方案
虽然 Deferred 模式的 promise 很有用,但现代 JavaScript 提供了一些替代方案:
- AbortController - 用于取消 fetch 请求
const controller = new AbortController(); fetch(url, { signal: controller.signal }); controller.abort(); // 取消请求- async/await - 简化异步流程控制
async function handleUserAction() { try { const result = await someAsyncOperation(); // 处理结果 } catch (error) { // 处理错误 } }- EventTarget - 用于事件驱动的场景
const eventTarget = new EventTarget(); eventTarget.addEventListener('done', () => { ... }); eventTarget.dispatchEvent(new Event('done')); -
8、deferred模式的 promise 状态控制权外置 注意事项
- 内存泄漏 - 长时间持有未解决的 Deferred 可能导致内存泄漏
- 状态单一性 - Promise 只能被解决或拒绝一次
- 错误处理 - 确保所有 reject 都被正确处理
- 竞态条件 - 在多线程环境(如 Web Worker)中要特别小心
-
9、总结
deferred模式的 promise 状态控制权外置是强大,特别适合需要从外部控制 Promise 解决时机的场景。虽然现代 JavaScript 提供了更多内置解决方案,但在某些情况下,该模式仍然是处理复杂异步流程的有效方法。使用时应当权衡其灵活性与潜在复杂度,选择最适合特定场景的方案。
-
10、拓展篇外
新版的es 提案中 新增的 promise 静态方法
Promise.withResolvers()方法也是和上述主题有一样的效果,感兴趣的大佬,可以自行mdn。同时感谢评论区大佬们提供的新的方法,拓展眼界!
兼容和polypfill,如下: