为什么我不能将 Concurrency 导入到我的 Swift 5.8 项目中?

Why can I not import Concurrency into my Swift 5.8 project?

提问人:e987 提问时间:5/4/2023 更新时间:5/5/2023 访问量:157

问:

我正在尝试编译这个简单的示例,首先Xcode 14.3(Swift 5.8)告诉我

No such module 'Concurrency'

然后,如果我将该行更改为导入_Concurrency,Xcode 14.3 会告诉我

Cannot find 'withTimeout' in scope

使用'withDeadline()'也是如此。怎么了?

import Foundation
import Concurrency

func fetchImageData() async throws -> Data {
    return try await withTimeout(1)  {
        // Simulate a long-running async operation
        let imageData = try await URLSession.shared.data(from: URL(string: "https://example.com/image.jpg")!)
        return imageData.0
    }
}
Swift 并发 超时 截止时间

评论

1赞 Itai Ferber 5/4/2023
Swift 没有模块,这就是为什么你不能导入它的原因;实际的模块确实被调用了,但你也不需要导入它,因为它默认是从 stdlib 导入和重新导出的(即,默认情况下它是导入的)。无论哪种方式,此模块都不提供 或 ;这些方法应该从何而来?您是否正在关注使用它们的教程或文章?Concurrency_ConcurrencywithTimeoutwithDeadline
0赞 e987 5/5/2023
@ItaiFerber我正在使用 Microsoft Edge 试图弄清楚如何使用 async/await 创建超时。如果有正确的方法可以做到这一点,请告诉我。
1赞 Joakim Danielson 5/5/2023
网络浏览器与此有什么关系?
0赞 Itai Ferber 5/5/2023
没有通用的方法可以对任意 ,因为在 Swift / 中,取消是“合作”;换句话说,任务本身需要不断检查“我被取消了吗?”,如果是这样,就停止做它正在做的事情。您能否分享要取消的操作的详细信息?(是你写的代码,还是第三方代码?Taskasyncawait
0赞 e987 5/5/2023
@ItaiFerber 我想解决的简单问题是,如果对 URL 的请求花费的时间超过 x 秒,我希望请求超时。

答:

1赞 Rob 5/5/2023 #1

Swift 并发不需要任何内容,因为它是为您导入的。import

但是没有功能。不过,你可以写一个。也许:withTimeout

extension Task where Failure == Error {
    /// Run asynchronous task, but cancel it if not finished within a certain period of time.
    ///
    /// - Parameters:
    ///   - duration: The duration before the task is canceled.
    ///   - operation: The async closure to run. The code therein should support cancelation.
    /// - Throws: If canceled, will throw `CancellationError`. If the task fails, this will throw the task’s error.
    /// - Returns: This will return the value returned by the `async` closure.

    static func withTimeout(_ duration: Duration, operation: @Sendable @escaping () async throws -> Success) async rethrows -> Success {
        let operationTask = Task.detached {
            try await operation()
        }

        let cancelationTask = Task<Void, Error>.detached {
            try await Task<Never, Never>.sleep(for: duration)
            operationTask.cancel()
        }

        return try await withTaskCancellationHandler {
            defer { cancelationTask.cancel() }
            return try await operationTask.value
        } onCancel: {
            operationTask.cancel()
        }
    }
}

毋庸置疑,我用更通用的 (即秒) 替换了 (即秒),并使其成为 的方法,但总体思路很简单:基本上,为提供的闭包启动一个任务,为取消另一个任务启动另一个任务,谁先完成将取消另一个任务。显然,如果 本身被取消,那么取消非结构化并发任务也是如此。TimeIntervalDurationstaticTaskwithTimeout

但不要迷失在这一点上的杂草中,因为人们可能会考虑这个主题有很多变化。

那么你可以这样称呼它:

func fetchImageData(from url: URL) async throws -> Data {
    try await Task.withTimeout(.seconds(1))  {
        try await URLSession.shared.data(from: url).0
    }
}

综上所述,/两者都已经有一个超时参数,所以你不妨使用它,也许:URLSessionURLRequest

nonisolated func fetchImageData(from url: URL) async throws -> Data {
    let request = URLRequest(url: url, timeoutInterval: 1)
    return try await URLSession.shared.data(for: request).0
}

评论

0赞 Itai Ferber 5/5/2023
这个版本太棒了;不过,我会考虑更突出地记录,如果不支持取消(无论是故意的,还是因为它只是不知道取消),超时将不适用。这可能非常微妙且有些危险,因此值得注意。在代码审查中,我什至建议将方法重命名为类似的东西,以便在调用站点上更明确(尽管我知道您只是使用帖子中介绍的名称。withTimeoutoperationperformCancellableWithTimeout(_:_:)
0赞 Rob 5/6/2023
在我的辩护中,我确实试图在文档中提到取消(这是这个一次性代码片段包含内联文档的唯一原因)。但我听到你说,这当然可以使它更加突出。
1赞 Itai Ferber 5/6/2023
哦,是的!不是为了批评(很高兴地投了赞成票!)——我只是通常非常厌恶这种事情的风险,所以对于任何将其集成到他们的代码库中的人来说,这是一个小建议。