用 Promise 封装事件 API 的核心是将事件触发转为可 await 的 Promise,需手动创建并用 resolve/reject 控制状态;支持一次监听(once)、超时保护(AbortController)及 Node.js EventEmitter 兼容方案。

用 Promise 封装基于事件的 API,核心是把“等待某个事件触发”变成“返回一个可 await 的 Promise”,让异步流程更线性、更易错误处理。关键在于手动创建 Promise,并在事件回调中调用 resolve 或 reject。
监听一次事件就完成:用 addEventListener + resolve
适用于只关心首次触发的事件(如 load、success、error)。
示例:封装 fetch 的替代方案——用 XMLHttpRequest 并返回 Promise:
function xhrGet(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
} else {
reject(new Error(`HTTP ${xhr.status}: ${xhr.statusText}`));
}
};
xhr.onerror = () => reject(new Error('Network error'));
xhr.send();
});
}
// 使用
xhrGet('/api/data')
.then(data => console.log(data))
.catch(err => console.error(err));
注意点:
- 必须在
onload和onerror中都调用resolve或reject,避免 Promise 永远 pending - 手动清理不是必须的(XHR 实例会自动回收),但复杂场景建议在 resolve/reject 后设
xhr = null
封装自定义事件或多次触发事件:用 once 或手动移除监听器
DOM 元素的 once: true 选项可自动移除监听器,适合只响应一次的事件:
function waitForClick(button) {
return new Promise(resolve => {
button.addEventListener('click', resolve, { once: true });
});
}
// 使用
waitForClick(document.getElementById('submit'))
.then(() => console.log('Clicked!'));
不支持 once 的环境(如某些旧版浏览器或自定义事件系统),需手动移除:
function waitForEvent(target, eventName) {
return new Promise(resolve => {
const handler = () => {
target.removeEventListener(eventName, handler);
resolve();
};
target.addEventListener(eventName, handler);
});
}
处理超时和取消:Promise.race + AbortController(推荐)
真实场景中,事件可能永不触发。加入超时保护更健壮:
function waitForEventWithTimeout(target, eventName, timeoutMs = 5000) {
const controller = new AbortController();
const eventPromise = new Promise(resolve => {
const handler = () => {
target.removeEventListener(eventName, handler, { signal: controller.signal });
resolve();
};
target.addEventListener(eventName, handler, { signal: controller.signal });
});
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
controller.abort();
reject(new Error(`Timeout after ${timeoutMs}ms`));
}, timeoutMs);
});
return Promise.race([eventPromise, timeoutPromise]);
}
说明:
- 利用
AbortController统一控制监听器生命周期,比手动removeEventListener更可靠 -
Promise.race确保最先完成的 Promise 决定结果,超时或事件任一发生即结束 - 现代浏览器均支持
AbortSignal传入addEventListener
封装 Node.js EventEmitter 类型(如 stream、net.Socket)
Node.js 中常见基于 EventEmitter 的 API,如 stream.Readable 的 'data' 或 'end' 事件:
const { once } = require('events');
// Node.js 15.4+ 内置的 once 函数,返回 Promise
async function readOnce(stream) {
const [chunk] = await once(stream, 'data');
return chunk;
}
// 手动封装(兼容旧版本)
function waitForEnd(stream) {
return new Promise(resolve => {
stream.once('end', resolve);
});
}
提示:
- Node.js ≥15.4 推荐直接用内置
events.once(),它已做好错误转发('error'会 reject) - 若需等待多个事件(如先 data 再 end),可用
Promise.all([once(...), once(...)])


















