在循环中调用具有完成处理程序的函数,按顺序处理结果。

huangapple go评论69阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2023年5月10日 23:11:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76220073.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定