В быстром, функция содержит петлю асинхронных вызовов, есть ли способ запустить по завершении ВСЕХ вызовов? [Дубликат]

Поскольку этот поток немного разветвился в общем обсуждении текущих реализаций с плавающей запятой, я бы добавил, что есть проекты по исправлению их проблем.

Взгляните на https: // posithub.org/, который демонстрирует тип номера, называемый posit (и его предшественник unum), который обещает предложить лучшую точность с меньшим количеством бит. Если мое понимание верное, оно также фиксирует проблемы в вопросе. Весьма интересный проект, человек, стоящий за ним, является математиком Dr. Джон Густафсон . Все это с открытым исходным кодом, с множеством реализаций в C / C ++, Python, Julia и C # ( https://hastlayer.com/arithmetics ).

104
задан Josh 10 March 2016 в 03:37
поделиться

7 ответов

Вы можете использовать группы отправки для запуска асинхронного обратного вызова, когда все ваши запросы завершатся.

Вот пример в Swift 4.1 (также работает в Swift 3), используя группы отправки выполняйте обратный вызов асинхронно, когда все сетевые запросы завершены.

override func viewDidLoad() {
    super.viewDidLoad()

    let myGroup = DispatchGroup()

    for i in 0 ..< 5 {
        myGroup.enter()

        Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
            print("Finished request \(i)")
            myGroup.leave()
        }
    }

    myGroup.notify(queue: .main) {
        print("Finished all requests.")
    }
}

Выход

Finished request 1
Finished request 0
Finished request 2
Finished request 3
Finished request 4
Finished all requests.

Для тех, кто использует более ранний Swift 2.3, вот пример, используя его синтаксис :

override func viewDidLoad() {
    super.viewDidLoad()

    let myGroup = dispatch_group_create()

    for i in 0 ..< 5 {
        dispatch_group_enter(myGroup)
        Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
            print("Finished request \(i)")
            dispatch_group_leave(self.myGroup)
        }
    }

    dispatch_group_notify(myGroup, dispatch_get_main_queue(), {
        print("Finished all requests.")
    })
}
207
ответ дан paulvs 25 August 2018 в 22:17
поделиться

Xcode 8.3.1 - Swift 3

Это принятый ответ paulvs, преобразованный в Swift 3:

let myGroup = DispatchGroup()

override func viewDidLoad() {
    super.viewDidLoad()

    for i in 0 ..< 5 {
        myGroup.enter()
        Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
            print("Finished request \(i)")
            myGroup.leave()
        }
    }

    myGroup.notify(queue: DispatchQueue.main, execute: {
        print("Finished all requests.")
    })
}
35
ответ дан Adam Bardon 25 August 2018 в 22:17
поделиться

Группа отправки хороша, но порядок отправленных запросов случайный.

Finished request 1
Finished request 0
Finished request 2

В моем случае проект, каждый запрос, который необходимо запустить, - это правильный порядок. Если это может кому-то помочь:

public class RequestItem: NSObject {
    public var urlToCall: String = ""
    public var method: HTTPMethod = .get
    public var params: [String: String] = [:]
    public var headers: [String: String] = [:]
}


public func trySendRequestsNotSent (trySendRequestsNotSentCompletionHandler: @escaping ([Error]) -> () = { _ in }) {

    // If there is requests
    if !requestItemsToSend.isEmpty {
        let requestItemsToSendCopy = requestItemsToSend

        NSLog("Send list started")
        launchRequestsInOrder(requestItemsToSendCopy, 0, [], launchRequestsInOrderCompletionBlock: { index, errors in
            trySendRequestsNotSentCompletionHandler(errors)
        })
    }
    else {
        trySendRequestsNotSentCompletionHandler([])
    }
}

private func launchRequestsInOrder (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], launchRequestsInOrderCompletionBlock: @escaping (_ index: Int, _ errors: [Error] ) -> Void) {

    executeRequest(requestItemsToSend, index, errors, executeRequestCompletionBlock: { currentIndex, errors in
        if currentIndex < requestItemsToSend.count {
            // We didn't reach last request, launch next request
            self.launchRequestsInOrder(requestItemsToSend, currentIndex, errors, launchRequestsInOrderCompletionBlock: { index, errors in

                launchRequestsInOrderCompletionBlock(currentIndex, errors)
            })
        }
        else {
            // We parse and send all requests
            NSLog("Send list finished")
            launchRequestsInOrderCompletionBlock(currentIndex, errors)
        }
    })
}

private func executeRequest (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], executeRequestCompletionBlock: @escaping (_ index: Int, _ errors: [Error]) -> Void) {
    NSLog("Send request %d", index)
    Alamofire.request(requestItemsToSend[index].urlToCall, method: requestItemsToSend[index].method, parameters: requestItemsToSend[index].params, headers: requestItemsToSend[index].headers).responseJSON { response in

        var errors: [Error] = errors
        switch response.result {
        case .success:
            // Request sended successfully, we can remove it from not sended request array
            self.requestItemsToSend.remove(at: index)
            break
        case .failure:
            // Still not send we append arror
            errors.append(response.result.error!)
            break
        }
        NSLog("Receive request %d", index)
        executeRequestCompletionBlock(index+1, errors)
    }
}

Вызов:

trySendRequestsNotSent()

Результат:

Send list started
Send request 0
Receive request 0
Send request 1
Receive request 1
Send request 2
Receive request 2
...
Send list finished

Подробнее см. Gist

0
ответ дан Aximem 25 August 2018 в 22:17
поделиться

Swift 3: Вы также можете использовать семафоры на этом пути. Это очень полезно, кроме того, вы можете точно отслеживать, когда и какие процессы будут завершены. Это было извлечено из моего кода:

    //You have to create your own queue or if you need the Default queue
    let persons = persistentContainer.viewContext.persons
    print("How many persons on database: \(persons.count())")
    let numberOfPersons = persons.count()

    for eachPerson in persons{
        queuePersonDetail.async {
            self.getPersonDetailAndSave(personId: eachPerson.personId){person2, error in
                print("Person detail: \(person2?.fullName)")
                //When we get the completionHandler we send the signal
                semaphorePersonDetailAndSave.signal()
            }
        }
    }

    //Here we will wait
    for i in 0..<numberOfPersons{
        semaphorePersonDetailAndSave.wait()
        NSLog("\(i + 1)/\(persons.count()) completed")
    }
    //And here the flow continues...
3
ответ дан freaklix 25 August 2018 в 22:17
поделиться

Для этой цели вам понадобятся семафоры.

 //Create the semaphore with count equal to the number of requests that will be made.
let semaphore = dispatch_semaphore_create(locationsArray.count)

        for key in locationsArray {       
            let ref = Firebase(url: "http://myfirebase.com/" + "\(key.0)")
            ref.observeSingleEventOfType(.Value, withBlock: { snapshot in

                datesArray["\(key.0)"] = snapshot.value

               //For each request completed, signal the semaphore
               dispatch_semaphore_signal(semaphore)


            })
        }

       //Wait on the semaphore until all requests are completed
      let timeoutLengthInNanoSeconds: Int64 = 10000000000  //Adjust the timeout to suit your case
      let timeout = dispatch_time(DISPATCH_TIME_NOW, timeoutLengthInNanoSeconds)

      dispatch_semaphore_wait(semaphore, timeout)

     //When you reach here all request would have been completed or timeout would have occurred.
5
ответ дан Shripada 25 August 2018 в 22:17
поделиться

Swift 3 или 4

Если вы не заботитесь о [заказах], используйте ответ @ paulvs , он отлично работает.

else на всякий случай, если кто-то хочет получить результат в порядке, а не стрелять одновременно, здесь - это код.

let dispatchGroup = DispatchGroup()
let dispatchQueue = DispatchQueue(label: "taskQueue")
let dispatchSemaphore = DispatchSemaphore(value: 0)

dispatchQueue.async {

    // use array categories as an example.
    for c in self.categories {

        if let id = c.categoryId {

            dispatchGroup.enter()

            self.downloadProductsByCategory(categoryId: id) { success, data in

                if success, let products = data {

                    self.products.append(products)
                }

                dispatchSemaphore.signal()
                dispatchGroup.leave()
            }

            dispatchSemaphore.wait()
        }
    }
}

dispatchGroup.notify(queue: dispatchQueue) {

    DispatchQueue.main.async {

        self.refreshOrderTable { _ in

            self.productCollectionView.reloadData()
        }
    }
}
5
ответ дан Timeless 25 August 2018 в 22:17
поделиться

Подробности

Xcode 9.2, Swift 4

Решение

class AsyncOperation {

    typealias NumberOfPendingActions = Int
    typealias DispatchQueueOfReturningValue = DispatchQueue
    typealias CompleteClosure = ()->()

    private let dispatchQueue: DispatchQueue
    private var semaphore: DispatchSemaphore

    private var numberOfPendingActionsQueue: DispatchQueue
    public private(set) var numberOfPendingActions = 0

    var whenCompleteAll: (()->())?

    init(numberOfSimultaneousActions: Int, dispatchQueueLabel: String) {
        dispatchQueue = DispatchQueue(label: dispatchQueueLabel)
        semaphore = DispatchSemaphore(value: numberOfSimultaneousActions)
        numberOfPendingActionsQueue = DispatchQueue(label: dispatchQueueLabel + "_numberOfPendingActionsQueue")
    }

    func run(closure: @escaping (@escaping CompleteClosure)->()) {

        self.numberOfPendingActionsQueue.sync {
            self.numberOfPendingActions += 1
        }

        dispatchQueue.async {
            self.semaphore.wait()
            closure {
                self.numberOfPendingActionsQueue.sync {
                    self.numberOfPendingActions -= 1
                    if self.numberOfPendingActions == 0 {
                        self.whenCompleteAll?()
                    }
                }
                self.semaphore.signal()
            }
        }
    }
}

Использование

let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
asyncOperation.whenCompleteAll = {
    print("All Done")
}
for i in 0...5 {
    print("\(i)")
    asyncOperation.run{ completeClosure in
        // add any (sync/async) code
        //..

        // Make signal that this closure finished
        completeClosure()
    }
}

Полный образец

import UIKit

class ViewController: UIViewController {

    let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 3, dispatchQueueLabel: "AnyString")
    let button = UIButton(frame: CGRect(x: 50, y: 80, width: 100, height: 40))
    let label = UILabel(frame: CGRect(x: 180, y: 50, width: 150, height: 100))

    var counter = 1
    var labelCounter = 0

    override func viewDidLoad() {
        super.viewDidLoad()

        button.setTitle("Button", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        label.text = "\(labelCounter)"
        label.numberOfLines = 2
        label.textAlignment = .natural
        view.addSubview(button)
        view.addSubview(label)
    }

    @objc func buttonTapped() {
        //sample1()
        sample2()
    }

    func sample1() {
        print("Sample 1")
        labelCounter += 1
        label.text = "button tapped \(labelCounter) times"

        print("Button tapped at: \(Date())")
        asyncOperation.whenCompleteAll = {
            print("All Done")
        }
        asyncOperation.run{ completeClosure in
            let counter = self.counter
            print("     - Loading action \(counter) strat at \(Date())")
            self.counter += 1

            DispatchQueue.global(qos: .background).async {
                sleep(1)
                print("     - Loading action \(counter) end at \(Date())")
                completeClosure()
            }
        }
    }

    func sample2() {
        print("Sample 2")
        label.text = ""
        asyncOperation.whenCompleteAll = {
            print("All Done")
        }

        for i in 0...5 {
            asyncOperation.run{ completeClosure in
                let counter = self.counter
                print("     - Loading action \(counter) strat at \(Date())")
                self.counter += 1

                DispatchQueue.global(qos: .background).async {
                    sleep(UInt32(i+i))
                    print("     - Loading action \(counter) end at \(Date())")
                    completeClosure()
                }
            }
        }

    }
}

Результаты

Образец 1

Образец 2

8
ответ дан Vasily Bodnarchuk 25 August 2018 в 22:17
поделиться