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

如何让自定义函数在Firebase的onComplete方法执行完毕后再返回结果?

问题本质

这是Firebase Firestore SDK的典型异步调用问题:docRef.get()是非阻塞的异步方法,调用后会立刻返回,请求逻辑在后台线程执行,onComplete回调要等请求完成后才会在主线程触发。所以你当前写法里的return语句永远会在回调修改value[0]之前执行,拿到的只能是初始赋值的true。


方案1:自定义回调接口(Java项目通用方案)

先定义一个结果回调接口:

// 定义回调接口,用来返回异步校验结果
public interface CheckMaxCallback {
    void onResult(boolean isExceedMaxLimit);
}

改造原有的checkMax方法,不要同步返回boolean值,改为通过回调返回结果:

private void checkMax(String email, CheckMaxCallback callback) {
    DocumentReference docRef = db.collection("user_bookings").document(email).collection(day).document("0");
    docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
        @Override
        public void onComplete(@NonNull Task<DocumentSnapshot> task) {
            boolean result;
            if (task.isSuccessful()) {
                DocumentSnapshot doc = task.getResult();
                if (doc.exists()) {
                    Long slot1 = doc.getLong("slot1");
                    Long slot2 = doc.getLong("slot2");
                    if(slot1 == -1 && slot2 == -1){
                        result = false;
                    } else if(slot1 == -1 || slot2 == -1){
                        if(durationPosition == 1){
                            Toast.makeText(BookingActivity.this, "超出最大可预订时长,请缩短预订时间", Toast.LENGTH_SHORT).show();
                            result = true;
                        } else {
                            result = false;
                        }
                    } else {
                        Toast.makeText(BookingActivity.this, "你所选日期的预订次数已达上限", Toast.LENGTH_SHORT).show();
                        result = true;
                    }
                } else {
                    result = false;
                    Log.e(TAG, "校验结果值 = " + result);
                    Log.d(TAG, "CHECKMAX 未找到对应文档");
                }
            } else {
                result = true;
                Log.d(TAG, "请求失败 ", task.getException());
            }
            // 将结果通过回调抛出
            callback.onResult(result);
        }
    });
}

调用方式改为传入回调处理后续逻辑:

// 原来调用 boolean res = checkMax(email); 的位置替换为下面的代码
checkMax(email, new CheckMaxCallback() {
    @Override
    public void onResult(boolean isExceedMaxLimit) {
        // 所有依赖校验结果的逻辑都要写在这里
        if (isExceedMaxLimit) {
            // 超出限制的处理逻辑
        } else {
            // 校验通过的处理逻辑
        }
    }
});

方案2:Kotlin协程方案(Kotlin项目适用)

如果你的项目使用Kotlin开发,可以配合Firebase官方KTX扩展用挂起函数同步写法实现异步逻辑,无需嵌套回调:

private suspend fun checkMax(email: String): Boolean {
    val docRef = db.collection("user_bookings").document(email).collection(day).document("0")
    return try {
        val doc = docRef.get().await()
        if (doc.exists()) {
            val slot1 = doc.getLong("slot1")
            val slot2 = doc.getLong("slot2")
            when {
                slot1 == -1L && slot2 == -1L -> false
                slot1 == -1L || slot2 == -1L -> {
                    if (durationPosition == 1) {
                        Toast.makeText(this@BookingActivity, "超出最大可预订时长,请缩短预订时间", Toast.LENGTH_SHORT).show()
                        true
                    } else false
                }
                else -> {
                    Toast.makeText(this@BookingActivity, "你所选日期的预订次数已达上限", Toast.LENGTH_SHORT).show()
                    true
                }
            }
        } else {
            false
        }
    } catch (e: Exception) {
        Log.d(TAG, "请求失败 ", e)
        true
    }
}

调用时在页面生命周期协程域中执行即可:

lifecycleScope.launch {
    val isExceedLimit = checkMax(email)
    // 处理校验结果
}

不推荐方案:CountDownLatch阻塞等待

不要在生产环境使用该方案,会阻塞主线程,请求超时或网络差时会直接触发ANR(应用无响应):

private boolean checkMax(String email){
    final boolean[] value = new boolean[1];
    CountDownLatch latch = new CountDownLatch(1);
    value[0] = true;

    DocumentReference docRef = db.collection("user_bookings").document(email).collection(day).document("0");
    docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
        @Override
        public void onComplete(@NonNull Task<DocumentSnapshot> task) {
            // 原有校验逻辑不变,处理完value[0]后执行计数减1
            if (task.isSuccessful()) {
                DocumentSnapshot doc = task.getResult();
                if (doc.exists()) {
                    Long slot1 = doc.getLong("slot1");
                    Long slot2 = doc.getLong("slot2");
                    if(slot1 == -1 && slot2 == -1){
                        value[0] = false;
                    }
                    else if(slot1 == -1 || slot2 == -1){
                        if(durationPosition == 1){
                            Toast.makeText(BookingActivity.this, "超出最大可预订时长,请缩短预订时间", Toast.LENGTH_SHORT).show();
                            value[0] = true;
                        }
                        else value[0] = false;
                    }
                    else{
                        Toast.makeText(BookingActivity.this, "你所选日期的预订次数已达上限", Toast.LENGTH_SHORT).show();
                        value[0] = true;
                    }

                } else {
                    value[0] = false;
                    Log.e(TAG, "校验结果值 = " + value[0]);
                    Log.d(TAG, "CHECKMAX 未找到对应文档");
                }
            } else {
                value[0] = true;
                Log.d(TAG, "请求失败 ", task.getException());
            }
            latch.countDown();
        }
    });
    try {
        latch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return value[0];
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 06:15:06