Completion gets called soon

ぐ巨炮叔叔 提交于 2021-02-15 07:43:45

问题


I have the following functions. I'm trying to pass allItems array into the completion block of requestItems, but I get a crash as it says it's nil. I removed the completion to check that item has a value and it does.

That means that the completion executes before the for loop.

Is there another approach for this? Something like Promises in Javascript that will execute the completion when the for loop has finished.

func requestItems(_ data: [String: Any], completion: (Bool, [Item]) -> Void) {

    var allItems = [Item]()

    for i in data["all"] {

        Routes.instance.getRequest(requestType: "items", params: nil, id: someId, completion: { item in

            var it = Item(item["name"] as! String)
            allItems.append(it)

        })

    }

    completion(true, allItems)
}

func getRoutes(requestType: String, parameters: [String: Any]?, id: String, completion: @escaping ([[String:Any]]) -> Void) {

    DispatchQueue.main.async {

        if id == "" {
            self.url = "\(URL_BASE)/\(requestType)"

        } else {
            self.url = "\(URL_BASE)/\(requestType)/\(id)"
        }

        Alamofire.request(self.url, method: .get, parameters: parameters, encoding: JSONEncoding.default, headers: self.headers).responseJSON { response in

            guard response.result.error == nil else {
                print(response.result.error!)
                return
            }

            switch response.result {

            case .success(let JSON):
                let response = [JSON] as! NSArray

                for item in response {

                    if let data = item as? [String: Any] {
                        print(data)
                    }
                }

                completion(response as! [[String : Any]])

            case .failure(let error):
                print("Request failed with error: \(error)")
            }
        }
    }

}

The completion handler executes too soon, returning a nil item


回答1:


You need DispatchGroup to get notified when the asynchronous loop is finished for example:

func requestItems(_ data: [String: Any], completion: (Bool, [Item]) -> Void) {

    var allItems = [Item]()
    let group = DispatchGroup()
    for i in data["all"] {
        group.enter()
        Routes.instance.getRequest(requestType: "items", params: nil, id: someId, completion: { item in

            let it = Item(item["name"] as! String)
            allItems.append(it)
            group.leave()

        })
    }
    group.notify(queue: DispatchQueue.main) {
        completion(true, allItems)
    }
}



回答2:


You are calling your completion handler outside the completion handler of the asynchronous request getRequest, so it will obviously return before the function would finish execution. Since you are calling several asynchronous requests in a row, a simple completion handler won't do the trick.

The best approach is to either use a DispatchQueue to only let your function return when all the requests are finished or to use a 3rd party framework, such as PromiseKit to handle the async functions are normal functions with return values.



来源:https://stackoverflow.com/questions/45484563/completion-gets-called-soon

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!