等待异步函数调用以完成

时间:2016-12-18 15:19:37

标签: ios swift uitableview

我之前已经问过这个问题,但所有解决方案对我都不起作用。

我有一个函数发送参数到API,并将数据作为列表返回,我有一个UITableView设置使用该列表,但它在列表分配给变量之前运行。

代码:

var functionResult = [String]()
override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        //gradesscrollView.contentSize.height = 3000
        fetchItems{ (str) in

            var returnedItems = [String]()

            let result = self.convertoArray(itemstoPass: str!)
            for i in result{
                functionResult.append(i)
            }
        }

        self.tableofItems.delegate = self
        self.tableofItems.dataSource = self //Data source is set up to use functionResult, however functionResult is empty before fetchItem runs.


}

如果不立即将其作为重复投票,我将不胜感激,这就是我所尝试过的。

  1. 派遣小组
  2. 信号量时间
  3. 运行变量
  4. 包括self.tableofItems.delegate = self& self.tableofItems.dataSource部分fetchItems{ (str) in =自我。
  5. 编辑: 已请求提取项目,

    func fetchItems(completionHandler: @escaping (String?) -> ()) -> () {
            let headers = [
                "Content-Type": "application/x-www-form-urlencoded"
            ]
            //Switch to keychain
            let username = UserDefaults.standard.object(forKey: "username") as! String?
            let password = UserDefaults.standard.object(forKey: "password") as! String?
    
            let usernametoSend = username!
            let passwordtoSend = password!
    
            print(usernametoSend)
            print(passwordtoSend)
            let parameters: Parameters = [
                "username": usernametoSend,
                "password": passwordtoSend
            ]
    
            Alamofire.request("https://www.mywebsite.com/API/getItems", method: .post, parameters: parameters, headers: headers)
                .responseString { response in
                    completionHandler(String(response.result.value!))
    

2 个答案:

答案 0 :(得分:1)

你不能 - 也不应该 - 等到异步调用完成。在理解之前,您需要学习异步编程。

异步功能接受要执行的作业,并在作业完成之前立即返回。

在Swift中,你通常编写一个异步函数来获取一个完成处理程序,这是一个你希望在异步任务完成时运行的代码块。

我在Github上有一个名为Async_demo(链接)的项目,用于说明这一点。它实现了一个处理异步下载的DownloadManager类。

关键部分是函数downloadFileAtURL(),它应该更恰当地命名为downloadDataAtURL,因为它返回内存数据而不是文件。

我创建了该函数以将完成处理程序作为参数:

/**
 This function demonstrates handling an async task.
 - Parameter url The url to download
 - Parameter completion: A completion handler to execute once the download is finished
 */

  func downloadFileAtURL(_ url: URL, completion: @escaping DataClosure) {

    //We create a URLRequest that does not allow caching so you can see the download take place
    let request = URLRequest(url: url,
                             cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
                             timeoutInterval: 30.0)
    let dataTask = URLSession.shared.dataTask(with: request) {
      //------------------------------------------
      //This is the completion handler, which runs LATER,
      //after downloadFileAtURL has returned.
      data, response, error in

      //Perform the completion handler on the main thread
      DispatchQueue.main.async() {
        //Call the copmletion handler that was passed to us
        completion(data, error)
      }
      //------------------------------------------
    }
    dataTask.resume()

    //When we get here the data task will NOT have completed yet!
  }
}

它使用NSURLSession从指定的URL下载数据块。我使用的数据请求调用需要一个在后台线程上执行的完成处理程序。在我传递给数据任务的完成处理程序中,我调用了传递给downloadFileAtURL()函数的完成处理程序,但是在主线程上。

您发布的代码有点令人困惑。目前还不清楚哪个部分是异步功能,流程是什么,或者显示表格视图需要什么数据。

如果您重写执行异步工作的函数以执行完成块,则可以在完成块中调用tableView.reloadData()。 (确保在主线程上执行调用。)

编辑:

正如其他人所说,您需要编辑问题以显示fetchItems()功能的代码。

我猜测该函数是执行异步工作的函数,并且它之后的块是异步执行的完成处理程序。如果是这样,你应该像这样重构你的代码:

var functionResult = [String]()
override func viewDidLoad() {
        super.viewDidLoad()

        //I moved these lines above the call to fetchItems to make it clear
        //that they run before fetchItems' completion closure is executed
        self.tableofItems.delegate = self
        self.tableofItems.dataSource = self //Data source is set up to use functionResult, however functionResult is empty before fetchItem runs.

        print("Step 1")
        fetchItems{ (str) in

            var returnedItems = [String]()

            let result = self.convertoArray(itemstoPass: str!)
            for i in result{
                functionResult.append(i)
            }
            print("Step 3")
            DispatchQueue.main.async() {
              tableview.reloadData() //Do this from the main thread, inside the closure of `fetchItems()`
            }
        }

        print("Step 2")
}

答案 1 :(得分:-2)

您应该使用loadView方法加载列表数据,以便在UITableView读取之前加载它。

有时,viewDidLoad表现得有点慢。通常,人们会在loadView的方法中初始化list或sth的数据,以确保在创建view之前完成数据。