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

如何让函数等待已存在的Promise完成?双按钮交互场景实现

问题

我开发的应用包含两个提交按钮:第一个按钮通过Promise发起需耗时数秒的API调用;第二个按钮收集用户输入后,需结合该API请求的输出进行处理。我希望实现以下流程:先启动API请求,用户完成选择后,点击第二个按钮时等待初始请求完成再执行后续逻辑。

相关代码如下:

this.upload.addEventListener('change', (event) => {

    ....
    
    // initial submit button
   
    API.getHumanFaceLandmarks(data.corrected).then((landmarks) => {
        
        console.log(landmarks); 

        this.humanFeatures = extractHumanFeatures.beginRender(
        landmarks,
        data.canvas,
        this.EYE_MASK,
        this.MOUTH_MASK,
        this.EYE_SELECTOR,
        this.MOUTH_SELECTOR);

    }).catch(error => console.log('error', error));

});

pet.addEventListener('click', (event) => {

    // second submit

    renderPetswitch.outputFinalImage(
        event.target.dataset.name,
        this.humanFeatures,    // <--- 如何等待这个值解析完成后再执行函数?
        this.OUTPUT_CANVAS);

})

API方法实现:

getHumanFaceLandmarks: async function(data) {
    
    console.log(data.substring(0, 50) + '...');

    const headers = new Headers();
    headers.append("Content-Type", "application/json");

    const body = JSON.stringify({
        "image": data
    });

    const requestOptions = {
        method: 'POST',
        headers: headers,
        body: body,
        redirect: 'follow'
    };

    const response = await fetch(constants.HUMAN_FACE_LANDMARKS_ENDPOINT, requestOptions);
    return response.json();

} 

请问在用户提交第二步操作时,如何让函数等待已存在的Promise完成?我希望优化用户体验,因此不想一次性完成所有请求。

解决方案

核心思路是保存第一个API请求返回的Promise对象,而非直接等待它完成后赋值。这样用户点击第二个按钮时,就能直接等待这个已存在的Promise——不管它已经完成还是正在执行。

步骤1:保存API请求的Promise链

修改第一个按钮的事件处理代码,把包含后续渲染逻辑的整个Promise链保存为实例变量:

this.upload.addEventListener('change', (event) => {
    ....
    
    // 保存完整的Promise链,让Promise最终解析为处理后的humanFeatures
    this.humanFeaturesPromise = API.getHumanFaceLandmarks(data.corrected)
        .then((landmarks) => {
            console.log(landmarks); 
            return extractHumanFeatures.beginRender(
                landmarks,
                data.canvas,
                this.EYE_MASK,
                this.MOUTH_MASK,
                this.EYE_SELECTOR,
                this.MOUTH_SELECTOR
            );
        })
        .catch(error => {
            console.log('error', error);
            // 抛出错误,让后续调用能捕获到异常
            throw error;
        });
});

步骤2:在第二个按钮中等待Promise完成

修改第二个按钮的点击事件,用async/await等待Promise完成后再执行后续逻辑:

pet.addEventListener('click', async (event) => {
    // 先判断是否存在有效请求,避免用户未执行第一步就点击第二步
    if (!this.humanFeaturesPromise) {
        console.log('请先完成第一步操作');
        return;
    }

    try {
        // 等待Promise完成,获取最终的humanFeatures
        const humanFeatures = await this.humanFeaturesPromise;
        // 执行最终渲染逻辑
        renderPetswitch.outputFinalImage(
            event.target.dataset.name,
            humanFeatures,
            this.OUTPUT_CANVAS
        );
    } catch (error) {
        console.log('处理失败:', error);
    }
});

额外优化:避免重复发起请求

如果用户多次点击第一个按钮,可以在赋值前判断Promise是否已存在,避免重复请求:

this.upload.addEventListener('change', (event) => {
    ....
    
    // 若已有未完成的请求,直接返回
    if (this.humanFeaturesPromise) {
        console.log('请求正在处理中,请稍后');
        return;
    }

    this.humanFeaturesPromise = API.getHumanFaceLandmarks(data.corrected)
        .then((landmarks) => {
            console.log(landmarks); 
            return extractHumanFeatures.beginRender(
                landmarks,
                data.canvas,
                this.EYE_MASK,
                this.MOUTH_MASK,
                this.EYE_SELECTOR,
                this.MOUTH_SELECTOR
            );
        })
        .catch(error => {
            console.log('error', error);
            throw error;
        })
        // 请求完成后清空Promise,允许下次重新发起
        .finally(() => {
            this.humanFeaturesPromise = null;
        });
});

这种方式既实现了“先启动API请求,用户选完后等待请求完成再执行第二步”的流程,还优化了重复点击的问题,完全匹配你的用户体验需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 16:05:24