You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

除了promisify,处理回调式异步函数还有更优方案吗?

问题:原生JavaScript支持显式控制的Promise(类似Assure/Deferred)吗?

我写了这样一个异步函数:

async function foo(axe: Axe): Promise<Sword> {
  // ...
}

它的使用方式如下:

async function bar() {
  // 获取一把axe...

  const sword = await foo(axe);

  // 使用sword执行操作...
}

目前运行正常,但实现foo时需要调用一个第三方库的回调式异步函数qux,而且我没法修改它的签名:

/* 用axe获取sword的最佳方式!
 * 若axe不够锋利则返回null */
function qux(axe: Axe, callback: (sword: Sword) => void);

我目前找到的可行方案是手动把qux包装成Promise:

async function foo(axe: Axe): Promise<Sword> {
  return new Promise<Sword>((resolve, reject) => {
    qux(axe, (sword) => {
      if (sword !== null) {
        resolve(sword);
      } else {
        reject('Axe is not sharp enough ;(');
      }
    });
  });
}

这种方式能工作,但我希望有更直观、可读性更强的写法。在一些语言里,可以创建类似Promise的Assure对象,之后在其他地方显式设置它的结果,比如这样:

async function foo(axe: Axe): Promise<Sword> {
  const futureSword = new Assure<Sword>();

  qux(axe, (sword) => {
    if (sword !== null) {
      futureSword.provide(sword);
    } else {
      futureSword.fail('Axe is not sharp enough ;(');
    }
  });

  return futureSword.promise;
}

这种写法原生JavaScript支持吗?还是必须借助Deferred类的第三方库?


更新:补充动机

为什么我更倾向第二种方案?核心是回调链嵌套的问题。

如果我需要在函数里执行多步异步操作而不只是调用qux,同步代码是这样的:

function zim(sling: Sling): Rifle {
  const bow = bop(sling);
  const crossbow = wug(bow);
  const rifle = kek(crossbow); 
  return rifle;
}

如果这些函数全是异步回调风格,用Promise包装会写出嵌套很深的代码:

async function zim(sling: Sling): Promise<Rifle> {
  return new Promise<Rifle>((resolve, reject) => {
    bop(sling, (bow) => {
      wug(bow, (crossbow) => {
        kek(crossbow, (rifle) => {
          resolve(rifle);
        });
      });
    });
  });
}

但用Assure的写法可以把代码扁平化:

async function zim(sling: Sling): Promise<Rifle> {
  const futureBow = new Assure<Bow>();
  bop(sling, (bow) => futureBow.provide(bow));

  const futureCrossbow = new Assure<Crossbow>();
  wug(await futureBow, (crossbow) => futureCrossbow.provide(crossbow));

  const futureRifle = new Assure<Rifle>();
  kek(await futureCrossbow, (rifle) => futureRifle.provide(rifle));
 
  return futureRifle;
}

这种写法不用跟踪嵌套作用域,维护起来更简单,如果函数有多个参数,优势会更明显。


思考

当然,嵌套回调的版本也有简洁性,不需要声明临时变量。

写这个问题的时候我想到另一种更符合JavaScript风格的写法:

async function zim(sling: Sling): Promise<Rifle> {
  const bow = await new Promise((resolve) => { bop(sling, resolve); });
  const crossbow = await new Promise((resolve) => { wug(bow, resolve); });
  const rifle = await new Promise((resolve) => { kek(crossbow, resolve); }); 
  return rifle;
}

这和Node.js的util.promisify用法类似,但可惜这些回调不是错误优先的模式。这种情况下,实现一个自定义的myPromisify来适配现有回调类型似乎更合理。


内容的提问来源于stack exchange,提问作者stanm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 04:07:09