繁体   English   中英

如何正确抛出并捕获来自 swift 异步 function 的错误

[英]how to properly throw and catch an error from swift async function

我有一个执行异步任务的 function。 有时该任务会失败并引发错误。 我无法从调用 function 中捕捉到该错误。下面的操场抓住了我遇到的问题的本质。

import UIKit

Task {
    var newNum: Double = 99.9
    do {
        newNum = try await getMyNumber()
        print("newNum within do: \(newNum)")
    } catch MyErrors.BeingStupid { //never gets caught
        print("caught being stupid")
    } catch MyErrors.JustBecause { //does get caught if throw is uncommented
        print("caught just because")
    }
    print("newNum outside of do \(newNum)")
}
print("done with main")

func getMyNumber() async throws -> Double {
    let retNum:Double = 0
    
    Task{
        sleep(5)
        let myNum: Double = Double.random(in: (0...10))
        if myNum > 9 {
            print("greater than 9")
        } else {
            print("less than 9 -- should throw")
            throw MyErrors.BeingStupid // error doesn't get thrown? HOW DO I CATCH THIS?
        }
    }
//    throw MyErrors.JustBecause  // this *does* get caught if uncommented
    return retNum //function always returns
}

enum MyErrors: Error {
    case BeingStupid, JustBecause
}

我如何捕获在调用 function 时在注释为“HOW DO I CATCH THIS”的行中抛出的错误?

Task用于非结构化并发 如果目的是模拟异步任务,我会建议保持在结构化并发中。 因此,使用Task.sleep(nanoseconds:)而不是sleep()并消除getMyNumber中的Task

func getMyNumber() async throws -> Double {
    try await Task.sleep(nanoseconds: 5 * NSEC_PER_SEC)   // better simulation of some asynchronous process

    let myNum = Double.random(in: 0...10)
    if myNum > 9 {
        print("greater than 9")
    } else {
        print("less than 9 -- should throw")
        throw MyErrors.beingStupid
    }

    return myNum
}

enum MyErrors: Error {
    case beingStupid, justBecause
}

如果您保持在结构化并发中,抛出的错误很容易被捕获。

有关结构化和非结构化并发之间差异的更多信息,请参阅Swift 编程指南:并发或 WWDC 2021 视频Explore structured concurrency in Swift


上面说明了标准的结构化并发模式。 如果你真的必须使用非结构化并发,你可以try await Task ,如果它没有抛出错误,返回它的value ,例如:

func getMyNumber() async throws -> Double {
    let task = Task.detached {
        sleep(5)                              // really bad idea ... never sleep ... especially never sleep on the main actor, which is why I used `Task.detached`

        let myNum = Double.random(in: 0...10)
        if myNum > 9 {
            print("greater than 9")
        } else {
            print("less than 9 -- should throw")
            throw MyErrors.beingStupid
        }

        return myNum
    }

    return try await task.value
}

请注意,因为我们正在运行一些缓慢且同步的东西,我们希望它在后台线程上运行,因此使用Task.detached

为了完整起见,我只包括这个非结构化并发示例。 您很可能希望保持结构化并发。 这样,您不仅可以享受更简洁的实现,还可以自动传播取消等。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM