英文:
In a loop, call a function having a completion handler, order the result
问题
I have the following problem, maybe there exists a solution. I need to call a given function with a completion handler in a loop from synchronous code. The problem is that the function calls might not complete in order, and I need to find a way to be able to know the order of the returned values.
func findNextWord(completionHandler: @escaping (String) -> Void) // Given
class A {
var words = [String]()
func retrieveAllWords() {
for _ in 0...20 {
findNextWord { [weak self] word in
self.words.append(word) // I want to be able to get the correct ordering in my array
}
}
}
What is the best way to do that? Thanks in advance.
Experimentations
Note: I tested a few things and I noticed that the closure displays variable a
with its value at the time the function was called. Maybe there is something to do with that.
import Foundation
class A {
let queue = DispatchQueue(label: "test")
var a = 0
func doInLoop() {
for _ in 0...5 {
doSomething()
}
}
func doSomething() {
a += 1
let a_not_self = a
print("outside a: \(a_not_self)")
orderPizza { [weak self] in
print("inside a: \(a_not_self)")
print("inside a (but self): \(self?.a)\n")
}
}
func orderPizza(completionHandler : @escaping () -> Void) {
queue.async {
sleep(5)
completionHandler()
}
}
}
var a = A()
a.doInLoop()
prints:
outside a: 1
outside a: 2
outside a: 3
outside a: 4
outside a: 5
outside a: 6
inside a: 1
inside a (but self): Optional(6)
inside a: 2
inside a (but self): Optional(6)
inside a: 3
inside a (but self): Optional(6)
inside a: 4
inside a (but self): Optional(6)
inside a: 5
inside a (but self): Optional(6)
inside a: 6
inside a (but self): Optional(6)
英文:
I have the following problem, maybe there exists a solution. I need to call a given function with a completion handler in a loop from synchronous code. The problem is that the function calls might not complete in order, and I need to find a way to be able to know the order of the returned values.
func findNextWord(completionHandler: @escaping (String) -> Void) // Given
class A {
var words = [String]()
func retrieveAllWords() {
for _ in 0...20 {
findNextWord { [weak self] word in
self.words.append(word) // I want to be able to get the correct ordering in my array
}
}
}
What is the best way to do that? Thanks in advance.
Experimentations
Note: I tested a few things and I noticed that the closure displays variable a
with its value at the time the function was called. Maybe there is something to do with that.
import Foundation
class A {
let queue = DispatchQueue(label: "test")
var a = 0
func doInLoop() {
for _ in 0...5 {
doSomething()
}
}
func doSomething() {
a += 1
let a_not_self = a
print("outside a: \(a_not_self)")
orderPizza { [weak self] in
print("inside a: \(a_not_self)")
print("inside a (but self): \(self?.a)\n")
}
}
func orderPizza(completionHandler : @escaping () -> Void) {
queue.async {
sleep(5)
completionHandler()
}
}
}
var a = A()
a.doInLoop()
prints:
outside a: 1
outside a: 2
outside a: 3
outside a: 4
outside a: 5
outside a: 6
inside a: 1
inside a (but self): Optional(6)
inside a: 2
inside a (but self): Optional(6)
inside a: 3
inside a (but self): Optional(6)
inside a: 4
inside a (but self): Optional(6)
inside a: 5
inside a (but self): Optional(6)
inside a: 6
inside a (but self): Optional(6)
答案1
得分: 1
你可以使用字典而非数组(或两者皆用)
class A {
var words = [Int: String]()
func retrieveAllWords() {
for index in 0...20 {
findNextWord { [weak self] word in
self.words[index] = word
}
}
}
英文:
You could use a dictionary instead of an array (or both)
class A {
var words = [Int: String]()
func retrieveAllWords() {
for index in 0...20 {
findNextWord { [weak self] word in
self.words[index] = word
}
}
}
答案2
得分: 0
> 怎样做是最好的呢?
最佳方式是使用 async/await
。
无需担心weak self
,也不必关心调用完成处理程序。只需退出方法或返回结果。循环将按顺序执行。
class A {
func doInLoop() async {
for i in 1...5 {
print("preparation \(i)")
await doSomething(i)
}
print("finished")
}
func doSomething(_ index: Int) async {
let result = await orderPizza(index)
print("completion: " + result)
}
func orderPizza(_ index: Int) async -> String {
try! await Task.sleep(for: .seconds(2))
return "Pizza \(index)"
}
}
Task {
let a = A()
await a.doInLoop()
}
(旧的) GCD 方法是使用 DispatchGroup
。
英文:
> What is the best way to do that?
The best way is async/await
.
You don't have to care about weak self
nor about calling completion handlers. Just exit the method or return something. The loop is being executed in order.
class A {
func doInLoop() async {
for i in 1...5 {
print("preparation \(i)")
await doSomething(i)
}
print("finished")
}
func doSomething(_ index: Int) async {
let result = await orderPizza(index)
print("completion: " + result)
}
func orderPizza(_ index: Int) async -> String {
try! await Task.sleep(for: .seconds(2))
return "Pizza \(index)"
}
}
Task {
let a = A()
await a.doInLoop()
}
The (old) GCD way is to use DispatchGroup
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论