JavaScript类混入:非async函数被包装后返回Promise的解决疑问
问题描述
我正在为JavaScript控制器类编写一个混入,实现调用指定类方法前自动等待某个异步函数解析,并将解析后的值作为最后一个参数传入原方法。现有useAwait实现如下,它会读取类的静态awaits属性,把目标方法包装成新的async函数,调用时传入原参数加上异步函数的解析结果:
const useAwait = (controller, asyncFn) => { controller.constructor.awaits.forEach(func => { const originalFunc = controller[func]; controller[func] = async (...args) => { return originalFunc.apply( controller, [...args, await asyncFn.call(controller)] ); }; }); }
在如下Controller实例上使用时:
class Controller { static awaits = ['foo', 'bar']; promise; constructor() { useAwait(this, this.load); } async foo(e, resolved) { return resolved; } bar(resolved) { return resolved; } async load() { if (!this.promise) { this.promise = new Promise(resolve => setTimeout(() => { resolve('Hello World!'); }, 3000)); } return this.promise; } }
遇到的问题:foo本身是async函数,运行正常;但bar原本是同步函数,被包装成async后返回值变成了Promise,直接调用bar会输出"[object Promise]"。我的疑问是:理论上是否需要判断原函数是否为async,若不是则await其返回值?
解答
不需要额外判断原函数是否为async,也不需要await它的返回值,核心原因和解决方案如下:
- async函数的固有特性:不管你return的是原始值还是Promise,async函数都会自动把返回值包装成Promise对象——这是语法层面的规则,和原函数是否为async无关。哪怕原函数
bar返回的是字符串'Hello World!',被async包装函数return后,依然会变成Promise<'Hello World!'>。 - 问题根源是调用方式:你看到的
"[object Promise]",是因为直接打印了包装后的bar函数返回的Promise对象,而非其解析后的值。正确的做法是用await bar()或者bar().then(res => console.log(res))来获取实际返回值。
如果业务场景硬要让部分方法保持同步返回(不推荐,因为混入的核心逻辑依赖异步前置操作,同步返回会导致无法拿到解析后的值),可以修改useAwait逻辑,但会破坏混入的一致性:
// 修改静态属性为配置对象,标记方法类型 class Controller { static awaits = { foo: 'async', bar: 'sync' }; } const useAwait = (controller, asyncFn) => { Object.entries(controller.constructor.awaits).forEach(([func, type]) => { const originalFunc = controller[func]; if (type === 'async') { controller[func] = async (...args) => { return originalFunc.apply(controller, [...args, await asyncFn.call(controller)]); }; } else { // 同步逻辑:无法等待异步操作,拿到的resolved是Promise而非解析后的值 controller[func] = (...args) => { const resolvedPromise = asyncFn.call(controller); return originalFunc.apply(controller, [...args, resolvedPromise]); }; } }); }
但这种方案存在逻辑矛盾:既然要依赖异步函数的解析结果,同步函数根本无法等待异步操作完成。所以更合理的选择是:接受所有被awaits标记的方法都会变成async函数的事实,调用时统一用await或.then()处理返回值。
内容的提问来源于stack exchange,提问作者gremo
相关产品推荐
相关产品推荐

