Promise 封装回调式异步

解释如何用 Promise 封装回调风格的异步操作,如 XMLHttpRequest、setTimeout 等。

#tech / dev / frontend #type / concept #status / growing #resource / javascript #resource / ecmascript

[!info] related notes

Promise 封装回调式异步

这篇笔记回答一个问题:如何把回调风格的异步 API 转换成 Promise 风格。

一句话定义

Promise 封装的核心是在 Promise 构造函数中执行回调式异步操作,成功时调用 resolve,失败时调用 reject。

核心模式

回调式异步和 Promise 的根本差异在于:回调是事件驱动的,Promise 是状态驱动的。封装的本质是把”事件结果”映射为”状态结果”。

封装 XMLHttpRequest

function ajaxPromise(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(xhr.statusText));
      }
    };
    xhr.onerror = () => reject(new Error('network error'));
    xhr.send();
  });
}

封装 setTimeout

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// 使用
await delay(1000);
console.log('1 秒后执行');

封装 Node.js 风格回调

function readFilePromise(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, 'utf8', (err, data) => {
      if (err) reject(err);
      else resolve(data);
    });
  });
}

通用 promisify 工具

function promisify(fn) {
  return function(...args) {
    return new Promise((resolve, reject) => {
      fn(...args, (err, result) => {
        if (err) reject(err);
        else resolve(result);
      });
    });
  };
}

常见边界

  • 如果回调会被多次调用(如事件监听),Promise 不适合,应该用 EventEmitter 或 Observable
  • Promise 只能 settle 一次,多次 resolve / reject 只有第一次生效
  • 封装时要注意清理资源(如 abort XHR、clearTimeout)

最小例子

function fetchJSON(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onload = () => resolve(JSON.parse(xhr.responseText));
    xhr.onerror = () => reject(new Error('failed'));
    xhr.send();
  });
}

fetchJSON('/api/data')
  .then(data => console.log(data))
  .catch(err => console.error(err));
创建于 2026/4/3 更新于 2026/5/27