【问题标题】:How to return failed task result in continuation task [duplicate]如何在继续任务中返回失败的任务结果[重复]
【发布时间】:2021-04-20 23:41:48
【问题描述】:

我正在使用 Kotlin 编写我的第一个应用程序,并且正在使用 Firestore 和 Firebase 存储。在删除文档的过程中,我想删除存储中文档引用的所有文件(因为在我的情况下它是对它们的唯一引用)。如果存储删除失败,我想中止文档删除,以避免存储中的孤立文件。我还想在“一个任务”中做所有事情,以允许正确显示进度条。我的简化代码如下所示:

    fun deleteItem(id: String): Task<Void>? {
        val deleteTask = deleteTaleMedia(id)
        continueWithTaskOrInNew(deleteTask) { task ->
            if (task?.isSuccessful != false) { ... }
        }
    }

    fun deleteItemMedia(id: String): Task<Void>? =
        getItem(id)?.continueWithTask { task ->
            if (task.isSuccessful)
                task.result?.toObject(ItemModel::class.java)?.let { deleteFiles(it.media) }
            else ???
        }

    fun deleteFiles(filesList: List<String>): Task<Void>? {
        var deleteTask: Task<Void>? = null
        for (file in filesList) deleteTask = continueWithTaskOrInNew(deleteTask) { task ->
            if (task?.isSuccessful != false) deleteFile(file)
            else task 
        }
        return task
    }

    fun deleteFile(fileName: String) = Firebase.storage.getReferenceFromUrl(fileName).delete()

    fun getItem(id: String): Task<DocumentSnapshot>? {
        val docRef = userDocRef?.collection(COLLECTION_PATH)?.document(id)
        return docRef?.get()
            ?.addOnCompleteListener { ... }
    }

    fun <ResultT, TContinuationResult> continueWithTaskOrInNew(
        task: Task<ResultT>?,
        continuation: (Task<ResultT>?) -> Task<TContinuationResult>?
    ) = task?.continueWithTask { continuation.invoke(task) } ?: continuation.invoke(null)

data class ItemModel(
    @DocumentId val id: String = "",
    var title: String = "",
    var media: List<String> = listOf()
)

我的问题来自deleteItemMedia 函数(末尾的“???”)。如果 get 任务失败,我想返回一个任务,告诉我的 deleteItem 函数中止删除 (task.isSuccessful == false)。我无法返回获取任务本身(将代码中的“???”替换为“任务”),因为它的类型(Task&lt;DocumentSnapshot&gt;)与删除任务的类型(Task&lt;Void&gt;)不同。我无法返回null,因为null 在根本没有媒体的情况下返回,这对我来说是有效的情况(应该删除文档)。有没有办法创建一个新的“失败的任务”?

【问题讨论】:

    标签: firebase kotlin google-cloud-firestore firebase-storage google-tasks-api


    【解决方案1】:

    在删除文档的过程中,我想删除存储中文档引用的所有文件(因为在我的情况下,这是对它们的唯一引用)。

    没有 API 可以做到这一点。您必须自己执行这两个删除操作。

    我也想在“一个任务”中做所有事情,以允许正确显示进度条。

    不幸的是,这不可能一次性完成。如果您想到原子操作,这也是不可能的,因为 Firebase 服务都不支持这种跨产品事务操作。您需要做的是,获取文档,获取对存储中文件的引用,删除文档,并在删除操作完成后立即删除文件。您绝对可以通过尝试从客户端回滚数据来降低风险,但您不能在“一个任务”中以原子方式执行它们。但是,在某个时间点,会出现客户端无法回滚的Exception。

    如果存储删除失败,我想中止文档删除,以避免存储中的孤立文件。

    为避免这种情况,首先,尽量不要有不完整的数据。例如,当您阅读文档并获得相应的存储 URL 时,不要盲目地假设所有这些文件都实际存在。文件可能由于多种原因不可用(之前已删除,由于某些原因服务不可用等)

    另一种方法可能是使用Cloud Functions for Firebase,这样您就可以删除所需的文档,并使用onDelete 函数从Storage 中删除相应的文件。这意味着,当文档删除失败时,存储中的文件不会被删除。如果删除文档操作成功,将触发云功能,将图片从存储中删除。这种方法将大大减少文档删除操作和从存储中删除文件之间出现故障的可能性,但它并不能 100% 消除这种可能性。

    除此之外,避免失败的最常见方法是使您的代码尽可能地健壮以防止失败并进行频繁的数据库清理

    【讨论】:

    • 感谢您的回答。我知道原子操作没有 API,这就是我自己执行操作的原因,如我共享的代码所示。我的方法是首先删除文件,并且只有在成功删除文档的情况下才删除(因为我更喜欢断开的链接而不是我永远找不到的孤立文件)。我的问题实际上是 Kotlin / Task API 问题,因为我遇到了从任务的延续任务(具有不同的结果类型)返回任务的成功状态的问题 - 有没有办法做到这一点?
    • 如果你想先删除文件,那么剩下的文件仍然需要清理。但是,如果您愿意,可以cancel 删除操作。
    • 嘿,我找到了解决方案,但无法在此处发布,因为我将其发布在更集中的 question 中。我刚刚签署了这个问题,与另一个问题重复。感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2017-01-30
    • 2021-06-05
    • 1970-01-01
    • 2023-03-14
    • 2011-07-20
    • 2012-12-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多