小编典典

等待异步函数调用完成

swift

我知道这个问题以前曾被问过,但是所有解决方案都不适合我。

我有一个将参数发送到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= self fetchItems{ (str) in

编辑:要求提取项目,

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!))

阅读 346

收藏
2020-07-07

共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")
}
2020-07-07