Promise状态控制权外置
Promise状态控制权外置
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 的状态变化
- 在某些情况下,希望在创建 Promise 时不立即执行某些逻辑,而是在稍后的某个时间点控制何时执行和如何执行。这时,可以通过将执行逻辑封装在另一个函数中,然后在需要的时候调用这个函数来控制 resolve 和 reject 的调用。
- 将 Promise 的 resolve 和 reject 控制权从 Promise 构造函数中分离出来的编程模式。它创建了一个包含 Promise 及其解决方法的对象,允许你在 Promise 外部控制其状态
5、现有解法
- 可以封装,拆解出 promise deferred对象
- deferred 对象通常包含三个部分:
- promise - 标准的 Promise 对象
- resolve - 用于解决 Promise 的函数
- reject - 用于拒绝 Promise 的函数
- 封装输出:
function getPromise() {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
- 与常态 Promise 的对比
| 特性 | 标准 Promise | Deferred 模式的 Promise |
|---|---|---|
| 控制权 | 在构造函数内部 | 外部可控制 |
| 解决时机 | 必须在构造函数中决定 | 可在任何时间、任何地点决定 |
| 适用场景 | 已知的异步操作 | 需要外部控制的异步操作 |
6、常用应用场景
6.1. 用户交互控制
function createConfirmationDialog() {
const {promise, resolve, reject} = getPromise();
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('用户取消');
}
});
6.2. 文件加载
function loadImg(){
const {promise, resolve, reject} = getPromise();
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, "图片加载失败了~~"))
6.3. 异步操作队列
class AsyncQueue {
constructor() {
this.queue = [];
this.current = null;
}
add(task) {
const deferredPromise = getPromise();
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());
}
}
6.4. API 请求超时控制
function fetchWithTimeout(url, timeout) {
const {promise, resolve, reject} = getPromise();
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() 方法也是和上述主题有一样的效果。
兼容和polypfill,如下:

