异步函数默认返回Promise,支持await;若需同步取值,应通过缓存机制(如闭包缓存Promise结果)或自定义Promise子类实现,而非强行同步等待。

一个异步函数默认返回 Promise,所以它天然支持 await;但“直接调用”(即不加 await)时,你拿到的是 Promise 对象本身,不是最终值——这不是“不能直接调用”,而是调用后行为不同。真正想问的通常是:如何让一个函数既能 await fn() 获取结果,又能在同步上下文中像普通函数一样使用(比如 fn() 直接返回值)?
核心思路:返回一个“可 await 也可同步取值”的对象
JavaScript 中无法让一个函数同时是同步的又返回 Promise,但可以通过封装返回一个自定义对象,让它:
- 作为 Promise 被
await(实现then、catch等) - 提供同步读取结果的方法(如
.value或.unwrap()),但需确保调用时 Promise 已完成 - 或更实用的方式:用
async函数 + 包装器,配合状态缓存(适合幂等、可缓存的场景)
方案一:返回带 .then 的 Promise,并缓存结果(推荐用于初始化/配置类场景)
适用于执行一次、结果可复用的异步操作(如加载配置、连接数据库)。通过闭包缓存 Promise 和最终值:
function createAsyncValue(asyncFn) {
let promise = null;
let result = undefined;
let error = undefined;
let resolved = false;
return function() {
if (!promise) {
promise = asyncFn().then(
(val) => { result = val; resolved = true; return val; },
(err) => { error = err; throw err; }
);
}
// 返回同一个 Promise,支持 await
const p = promise;
// 扩展 Promise 实例,添加同步访问能力(仅当已完成)
if (resolved) {
p.value = result;
p.unwrap = () => result;
p.isResolved = true;
} else {
p.isResolved = false;
p.unwrap = () => { throw new Error('Promise not settled yet'); };
}
return p;
};
}
// 使用示例
const fetchUser = createAsyncValue(() => fetch('/api/user').then(r => r.json()));
// ✅ 可 await
const user = await fetchUser();
// ✅ 也可直接调用,后续调用能同步取值(前提是已 resolve 过)
if (fetchUser().isResolved) {
console.log(fetchUser().value); // 不再发起请求,直接拿缓存值
}
方案二:返回 Promise 子类(高级,需谨慎)
继承 Promise,重写 then 并添加同步属性(如 .value)。但注意:Promise 构造器内部状态不可直接暴露,必须靠 .then 回调捕获结果,因此 .value 只能在 resolve 后安全读取:
class AsyncValue extends Promise {
constructor(executor) {
let _resolve, _reject;
super((resolve, reject) => {
_resolve = resolve;
_reject = reject;
executor(resolve, reject);
});
this._value = undefined;
this._error = undefined;
this._settled = false;
const originalThen = this.then.bind(this);
this.then = (onFulfilled, onRejected) => {
const wrappedFulfilled = (val) => {
this._value = val;
this._settled = true;
return onFulfilled?.(val);
};
const wrappedRejected = (err) => {
this._error = err;
this._settled = true;
return onRejected?.(err);
};
return originalThen(wrappedFulfilled, wrappedRejected);
};
Object.defineProperty(this, 'value', {
get() {
if (!this._settled) throw new Error('Not settled yet');
if (this._error) throw this._error;
return this._value;
}
});
}
}
// 创建函数
function asyncFn() {
return new AsyncValue((resolve) =>
setTimeout(() => resolve('done'), 100)
);
}
// ✅ await 正常工作
console.log(await asyncFn()); // 'done'
// ✅ 同步取值(仅在 resolve 后)
const p = asyncFn();
setTimeout(() => {
console.log(p.value); // 'done' —— 成功读取
}, 200);
方案三:不推荐的做法——试图“同步等待”
不要用 Atomics.wait、while(!done) 或 eval(`await ${...}`) 等方式强行同步阻塞。这会冻结主线程、破坏异步模型,违背 JavaScript 设计原则,在浏览器中尤其危险。
如果你的业务逻辑**必须同步执行**,说明设计上应重新评估:是否本就不该是异步?能否把异步前置(如启动时预加载),后续全部走同步路径?
本质上,JavaScript 的异步本质决定了“真正同步取值”和“原生 await 支持”无法在单个裸函数上无缝共存。最自然、健壮的做法是:明确区分场景——该 await 的地方就 await,需要同步值的地方,确保它来自已 resolve 的缓存或初始化完成的状态。










