等待异步函数调用完成

Posted

技术标签:

【中文标题】等待异步函数调用完成【英文标题】:Waiting for Asynchronous function call to complete 【发布时间】:2016-12-18 15:19:36 【问题描述】:

我知道以前有人问过这个问题,但所有解决方案都不适用于我。

我有一个向 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.



如果它没有立即被投票为重复,我将不胜感激,这是我尝试过的。

    调度组 信号量计时 运行变量 在fetchItems (str) in 部分中包括self.tableofItems.delegate = self 和self.tableofItems.dataSource = self。

编辑: 已请求获取项目,

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

【问题讨论】:

你有没有试过在获取项目后在主线程中重新加载表格视图? 显示fetchItems()implementation。 并且:tableofItems.reloadData() 直接在您的 for 循环之后。 shallowThought,不幸的是 reloadData() 不起作用,我在重新加载打印语句之前验证了该列表已填充 【参考方案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")

【讨论】:

对不起,我对 Swift 很陌生 但是,还有吗?我有点需要为程序做这件事,我来自 Python 背景,一切都是一行一行的 查看我的答案的编辑。我对您的代码在做什么进行了有根据的猜测,并根据该猜测提供了解决方案。 Duncan C,你的代码成功了!对不起,如果这个问题很愚蠢。我对应用程序开发非常陌生,我很高兴学习。我希望有一天我不必那么频繁地问有关 Stack Overflow 的问题。非常感谢您的帮助! 好的,为什么要投反对票?如果您认为我的回答有待改进,请发表评论说明。我正在努力提供帮助。【参考方案2】:

您应该在loadView的方法中加载列表的数据,以便在UITableView读取之前提前加载。

有时,viewDidLoad 执行速度有点慢。一般人们会在loadView的方法中初始化list或某某的数据,以确保数据在view创建之前完成。

【讨论】:

我尝试添加 loadView 功能,但问题仍然存在 你实现了UITableView的相关方法吗?比如有多少rows,有多少cells 对不起,这个答案没有意义,并且提供了误导性信息。 实现loadView()实际上并不常见,如果你不知道它的作用,就会充满问题,不要打电话给super.loadView() 为什么你对我的回答投了反对票?有些问题需要尝试才能解决。

以上是关于等待异步函数调用完成的主要内容,如果未能解决你的问题,请参考以下文章

等待异步函数在 Angular 中完成

每个数组元素的异步调用并等待完成[重复]

python实现异步调用函数执行

DispatchQueue 不等待异步函数完成

在映射下一项之前,异步等待映射不等待异步函数在映射函数内部完成

异步等待不等待完成