除了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
相关产品推荐
相关产品推荐

