ios 如何在 Swift 中进行异步/等待?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/48713427/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make async / await in Swift?
提问by cusmar
I would like to simulate async and await request from Javascript to Swift 4. I searched a lot on how to do it, and I thought I found the answer with DispatchQueue
, but I don't understand how it works.
我想模拟从 Javascript 到 Swift 4 的异步和等待请求。我搜索了很多关于如何做到这一点的方法,我以为我找到了答案DispatchQueue
,但我不明白它是如何工作的。
I want to do a simple stuff:
我想做一个简单的事情:
if let items = result.value {
var availableBornes = [MGLPointFeature]()
for item in items {
guard let id = item.id else { continue }
let coordinate = CLLocationCoordinate2D(latitude: Double(coor.x), longitude: Double(coor.y))
// ...
// This is an asynchronous request I want to wait
await _ = directions.calculate(options) { (waypoints, routes, error) in
guard error == nil else {
print("Error calculating directions: \(error!)")
return
}
// ...
if let route = routes?.first {
let distanceFormatter = LengthFormatter()
let formattedDistance = distanceFormatter.string(fromMeters: route.distance)
item.distance = formattedDistance
// Save feature
let feature = MGLPointFeature()
feature.attributes = [
"id": id,
"distance": formattedDistance
]
availableBornes.append(feature)
}
}
}
// This should be called after waiting for the async requests
self.addItemsToMap(availableBornes: availableBornes)
}
What should I do?
我该怎么办?
回答by cusmar
Thanks to vadian's comment, I found what I expected, and it's pretty easy. I use DispatchGroup()
, group.enter()
, group.leave()
and group.notify(queue: .main){}
.
感谢vdian的评论,我找到了我所期望的,而且很容易。我使用DispatchGroup()
, group.enter()
,group.leave()
和group.notify(queue: .main){}
。
func myFunction() {
let array = [Object]()
let group = DispatchGroup() // initialize
array.forEach { obj in
// Here is an example of an asynchronous request which use a callback
group.enter() // wait
LogoRequest.init().downloadImage(url: obj.url) { (data) in
if (data) {
group.leave() // continue the loop
}
}
}
group.notify(queue: .main) {
// do something here when loop finished
}
}
回答by brandonscript
(Note: Swift 5 may support await
as you'd expect it in ES6!)
(注意:Swift 5 可能会await
像您期望的那样在 ES6 中提供支持!)
What you want to look into is Swift's concept of "closures". These were previously known as "blocks" in Objective-C, or completion handlers.
您要研究的是 Swift 的“闭包”概念。这些以前在 Objective-C 中称为“块”或完成处理程序。
Where the similarity in JavaScript and Swift come into play, is that both allow you to pass a "callback" function to another function, and have it execute when the long-running operation is complete. For example, this in Swift:
JavaScript 和 Swift 的相似之处在于,两者都允许您将“回调”函数传递给另一个函数,并在长时间运行的操作完成时执行。例如,这在 Swift 中:
func longRunningOp(searchString: String, completion: (result: String) -> Void) {
// call the completion handler/callback function
completion(searchOp.result)
}
longRunningOp(searchString) {(result: String) in
// do something with result
}
would look like this in JavaScript:
在 JavaScript 中看起来像这样:
var longRunningOp = function (searchString, callback) {
// call the callback
callback(err, result)
}
longRunningOp(searchString, function(err, result) {
// Do something with the result
})
There's also a few libraries out there, notably a new one by Google that translates closures into promises: https://github.com/google/promises. These might give you a little closer parity with await
and async
.
还有一些库,特别是谷歌的一个新库,它将闭包转换为承诺:https: //github.com/google/promises。这些可能会让你更接近await
和async
。
回答by SergPanov
You can use semaphores to simulate async/await.
您可以使用信号量来模拟 async/await。
func makeAPICall() -> Result <String?, NetworkError> {
let path = "https://jsonplaceholder.typicode.com/todos/1"
guard let url = URL(string: path) else {
return .failure(.url)
}
var result: Result <String?, NetworkError>!
let semaphore = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: url) { (data, _, _) in
if let data = data {
result = .success(String(data: data, encoding: .utf8))
} else {
result = .failure(.server)
}
semaphore.signal()
}.resume()
_ = semaphore.wait(wallTimeout: .distantFuture)
return result
}
And here is example how it works with consecutive API calls:
以下是它如何处理连续 API 调用的示例:
func load() {
DispatchQueue.global(qos: .utility).async {
let result = self.makeAPICall()
.flatMap { self.anotherAPICall(func awaitAPICall(_ url: URL) throws -> String? {
let future = URLSession.shared.dataTaskFuture(for: url)
let data = try future.await().data
return String(data: data, encoding: .utf8)
}
func load(url: URL) {
DispatchQueue.main.startCoroutine {
let result1 = try self.awaitAPICall(url)
let result2 = try self.awaitAPICall2(result1)
let result3 = try self.awaitAPICall3(result2)
print(result3)
}
}
) }
.flatMap { self.andAnotherAPICall(Future<Feature, Error> { promise in
directions.calculate(options) { (waypoints, routes, error) in
if let error = error {
promise(.failure(error))
}
promise(.success(routes))
}
}
.flatMap { routes in
// extract feature from routes here...
feature
}
.receiveOn(DispatchQueue.main) // UI updates should run on the main queue
.sink(receiveCompletion: { completion in
// completion is either a .failure or it's a .success holding
// the extracted feature; if the process above was successful,
// you can now add feature to the map
}, receiveValue: { _ in })
.store(in: &self.cancellables)
) }
DispatchQueue.main.async {
switch result {
case let .success(data):
print(data)
case let .failure(error):
print(error)
}
}
}
}
Here is the articledescribing it in details.
这是详细描述它的文章。
And you can also use primises with PromiseKit and similar libraries
您还可以将 primises 与 PromiseKit 和类似的库一起使用
回答by Alex Belozierov
You can use this framework for Swift coroutines - https://github.com/belozierov/SwiftCoroutine
您可以将此框架用于 Swift 协程 - https://github.com/belozierov/SwiftCoroutine
Unlike DispatchSemaphore, when you call await it doesn't block the thread but only suspends coroutine, so you can use it in the main thread as well.
与 DispatchSemaphore 不同的是,当您调用 await 时,它不会阻塞线程,而只会挂起协程,因此您也可以在主线程中使用它。
##代码##回答by Bill
In iOS 13 and up, you can now do this using Combine. Future
is analogous to async
and the flatMap
operator on publishers (Future
is a publisher) is like await
. Here's an example, loosely based on your code:
在 iOS 13 及更高版本中,您现在可以使用组合来执行此操作。Future
类似于出版商(是出版商)上async
的flatMap
运营商Future
就像await
。这是一个示例,大致基于您的代码: