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

如何在JavaScript中封装async/await,让调用代码无需使用await?

核心结论

完全将异步细节从调用代码中抽象、实现真正同步的loadSavedItems方法不可行。因为JavaScript在浏览器环境中是单线程模型,网络请求这类IO操作必须是非阻塞的异步执行,强行转为同步会阻塞主线程,导致页面卡死、失去响应。

不过我们可以通过一些方案简化调用逻辑,尽量让调用者少处理异步细节,以下是具体实现方式:

方案一:初始化预加载数据

在Api类构造函数中提前发起请求并缓存数据,后续调用loadSavedItems直接返回同步的缓存结果:

export class Api {
    #savedItems = null;
    #loadPromise = null;

    constructor() {
        // 构造时自动开始加载数据
        this.#loadPromise = this.#apiGet('saved-items/').then(data => {
            this.#savedItems = data;
        }).catch(err => {
            console.error('加载保存项失败:', err);
        });
    }

    async #apiGet(endpoint) {
        const response = await fetch('/api/' + endpoint);
        return await response.json();
    }

    loadSavedItems() {
        // 如果已加载完成,直接返回数据
        if (this.#savedItems) {
            return this.#savedItems;
        }
        // 若仍在加载中,返回Promise(可选:调用者可选择等待,或在初始化完成后再调用)
        return this.#loadPromise.then(() => this.#savedItems);
    }
}

调用示例:

const api = new Api();
// 页面初始化完成后调用(此时数据大概率已加载完毕)
document.addEventListener('DOMContentLoaded', () => {
    const savedItems = api.loadSavedItems();
    console.log(savedItems); // 直接拿到数据
});

方案二:使用回调函数

将数据处理逻辑封装到回调中,调用者无需手动处理Promise:

export class Api {
    async #apiGet(endpoint) {
        const response = await fetch('/api/' + endpoint);
        return await response.json();
    }

    loadSavedItems(onSuccess, onError) {
        this.#apiGet('saved-items/')
            .then(data => onSuccess(data))
            .catch(err => onError?.(err) || console.error('加载失败:', err));
    }
}

调用示例:

const api = new Api();
api.loadSavedItems(
    savedItems => {
        // 成功获取数据后执行逻辑
        console.log(savedItems);
    },
    error => {
        // 错误处理逻辑
        alert('加载保存项失败');
    }
);

方案三:基于事件通知

让Api类继承EventTarget,通过自定义事件通知数据加载结果:

export class Api extends EventTarget {
    async #apiGet(endpoint) {
        const response = await fetch('/api/' + endpoint);
        return await response.json();
    }

    loadSavedItems() {
        this.#apiGet('saved-items/')
            .then(data => {
                this.dispatchEvent(new CustomEvent('savedItemsLoaded', { detail: data }));
            })
            .catch(err => {
                this.dispatchEvent(new CustomEvent('savedItemsError', { detail: err }));
            });
    }
}

调用示例:

const api = new Api();
// 监听成功事件
api.addEventListener('savedItemsLoaded', e => {
    const savedItems = e.detail;
    console.log(savedItems);
});
// 监听错误事件
api.addEventListener('savedItemsError', e => {
    console.error('加载失败:', e.detail);
});
// 发起请求
api.loadSavedItems();

关于同步XHR的说明

虽然早期XMLHttpRequest支持同步模式,但强烈不推荐使用:它会阻塞主线程,导致页面无法响应用户操作(比如点击、滚动),直到请求完成,严重破坏用户体验。现代浏览器也已在多个场景中限制了同步XHR的使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 21:42:21