Swift 如何创建从异步调用返回值的函数。

Swift 如何创建从异步调用返回值的函数。,swift,Swift,所以我有这个POST函数 class func postRequest(request: URLRequest, saveCookie: Bool, completionHandler: @escaping (_ postRequestStatus: [String:Any]) -> ()) { let session = URLSession.shared //So now no need of type conversion let task = session

所以我有这个POST函数

class func postRequest(request: URLRequest, saveCookie: Bool, completionHandler: @escaping (_ postRequestStatus: [String:Any]) -> ()) {
    let session = URLSession.shared
    //So now no need of type conversion
    let task = session.dataTask(with: request) {
        (data, response, error) in
        func displayError(_ error: String) {
            print(error)
        }

        /* GUARD: Was there an error? */
        guard (error == nil) else {
            displayError("There was an error with your request: \(String(describing: error))")
            return
        }

        guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else {
            displayError("Your request returned a status code other than 2xx!")
            return
        }

        /* GUARD: Was there any data returned? */
        guard let data = data else {
            displayError("No data was returned by the request!")
            return
        }

        /* Since the incoming cookies will be stored in one of the header fields in the HTTP Response,parse through the header fields to find the cookie field and save the data */
        if saveCookie{
            let httpResponse: HTTPURLResponse = response as! HTTPURLResponse
            let cookies = HTTPCookie.cookies(withResponseHeaderFields: httpResponse.allHeaderFields as! [String : String], for: (response?.url!)!)
            HTTPCookieStorage.shared.setCookies(cookies as [AnyObject] as! [HTTPCookie], for: response?.url!, mainDocumentURL: nil)
        }

        let json: [String:Any]?
        do
        {
            json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String:Any] ?? [:]
        }
        catch
        {
            displayError("Could not parse the data as JSON: '\(data)'")
            return
        }

        guard let server_response = json else
        {
            displayError("Could not parse the data as JSON: '\(data)'")
            return
        }

        if let userID = server_response["UserID"] as? Int64 {
            print(userID)
            completionHandler(server_response)
        }else{
            displayError("Username or password incorrect.")
        }
    }
    return task.resume()
}
以上提到的函数属于同一类。现在我想在另一个类中编写一个函数,这样我就可以调用loginPostRequest(),并得到[String:Any]作为结果。大概是这样的:

 var post_data = [String:String]()
    post_data["username"] = "Email"
    post_data["password"] = "Password"
data :[String:Any] = HTTPRequests.loginPostRequest(post_data);

如何更新其他函数,以便我可以实现上述“数据”作为返回值,而不必担心这里的完成处理程序

您的代码非常具体,但我将提供一个更通用的解决方案,这样它也可以帮助其他偶然发现这一点的人。如果您在实施过程中有后续问题,请随时提问

var post_data = [String:String]()
post_data["Email"] = "isername@hotmail.com"
post_data["Password"] = "password1234"
HTTPRequests.loginPostRequest(post_data: post_data, completionHandler: { postRequestStatus in
    let data = postRequestStatus
    print(data)
})
首先,如果您的目标是获得同步行为,请使用同步方法。许多库提供异步和同步变体的函数。他们没有必要执行额外的工作来使调用异步,只需要您做更多的工作来为它进行同步包装

您可以将任何要使用完成句柄的异步调用转换为返回值的同步调用。只需遵循以下模式:

func synchronousFunction(_ someInput: Input, timeout: DispatchTime? = nil) -> Result {
    var result: Result?

    let semaphore = DispatchSemaphore(value: 0)

    asynchronousFunction(input, completionHandler: { _result in
        result = _result
        semaphore.signal()
        // signalling the semaphore indicates the async function is done,
        // that `result` is set, and that `synchronousFunction` can return it.
    })

    // Wait for signal from completion handler, up to `timeout`, if it's set
    if let timeout = timeout  { semaphore.wait(timeout: timeout) }
    else { semaphore.wait() }


    if let result = result { return result }
    else { fatalError("The completion handler didn't assign to `result`.") }
}

如果
DispatchTime
对您来说不够准确,您也可以使用
DispatchWallTime
。如果您确信异步任务将完成,也可以完全删除超时逻辑。

有两种不同的方法可以做到这一点,因为我写了这篇文章,给出了一个解决方案,但我认为有不止一种方法可以剥猫皮。。那么为什么我们没有选择呢

我只是猜测一下,并没有尝试自己实现它,但实现跨线程通信的一种方法是使用协议。这需要关联的类(委托)符合方法/变量。“manager”类可以使用这些必需的方法/变量将数据传递给委托。也许是这样的?(如果这实际上是您想要实现的,那么是否不使用来自外部类的处理程序?)

协议/“管理器”:

//protocol, required vars/func a delegate must have (conform to)
protocol HTTPRequestDelegate {
    var post_data: [String: String] { get }
    func httpRequestDelegate(receivedData: [String: Any])
}

//manager class, acts on delegate via protocol methods/vars
class HTTPRequest: NSObject {  //Im just using the NSObject super

    //declare a property the delegate can access and assign as themself
    var delegate: HTTPRequestDelegate? {
        didSet {
            //once a delegate assigns itself as the delegate, execute the desired class func   
            guard let delegate = delegate else { return }
            HTTPRequest.loginPostRequest(post_data: delegate.post_data, delegate: delegate)
        }
    }


    class func postRequest(request: URLRequest, saveCookie: Bool, completionHandler: @escaping (_ postRequestStatus: [String:Any]) -> ()) {
        //class setup
    }


    // Since you are using class func's, add a parameter to bring in the delegate's of the class
    class func loginPostRequest(post_data: [String:String], delegate: HTTPRequestDelegate) {
        let url:  URL = URL(string: "theURL")!
        let request = URLRequest(url: url)

        postRequest(request: request, saveCookie: true, completionHandler: { postRequestStatus in
                    // once the data has been received, send the data to the delegate via protocol func
                    delegate.httpRequestDelegate(receivedData: postRequestStatus)
        })
    }
}
//delegate class, the class you want to receiver to send info to  
class FooClass: NSObject, HTTPRequestDelegate {

    var post_data: [String: String] { return postData }
    var postData = [String: String]()
    var data: [String: Any]?
    let requestManager = HTTPRequest()

    override init() {
        super.init()
        self.postData["username"] = "Email"
        self.postData["password"] = "Password"
        requestManager.delegate = self
    }

    func httpRequestDelegate(receivedData: [String : Any]) {
        //when the httpRequest class receives the data, it will call this call this function and send the info to the delegate(s) class as parameters.
        data = receivedData
    }
}
代理类:

//protocol, required vars/func a delegate must have (conform to)
protocol HTTPRequestDelegate {
    var post_data: [String: String] { get }
    func httpRequestDelegate(receivedData: [String: Any])
}

//manager class, acts on delegate via protocol methods/vars
class HTTPRequest: NSObject {  //Im just using the NSObject super

    //declare a property the delegate can access and assign as themself
    var delegate: HTTPRequestDelegate? {
        didSet {
            //once a delegate assigns itself as the delegate, execute the desired class func   
            guard let delegate = delegate else { return }
            HTTPRequest.loginPostRequest(post_data: delegate.post_data, delegate: delegate)
        }
    }


    class func postRequest(request: URLRequest, saveCookie: Bool, completionHandler: @escaping (_ postRequestStatus: [String:Any]) -> ()) {
        //class setup
    }


    // Since you are using class func's, add a parameter to bring in the delegate's of the class
    class func loginPostRequest(post_data: [String:String], delegate: HTTPRequestDelegate) {
        let url:  URL = URL(string: "theURL")!
        let request = URLRequest(url: url)

        postRequest(request: request, saveCookie: true, completionHandler: { postRequestStatus in
                    // once the data has been received, send the data to the delegate via protocol func
                    delegate.httpRequestDelegate(receivedData: postRequestStatus)
        })
    }
}
//delegate class, the class you want to receiver to send info to  
class FooClass: NSObject, HTTPRequestDelegate {

    var post_data: [String: String] { return postData }
    var postData = [String: String]()
    var data: [String: Any]?
    let requestManager = HTTPRequest()

    override init() {
        super.init()
        self.postData["username"] = "Email"
        self.postData["password"] = "Password"
        requestManager.delegate = self
    }

    func httpRequestDelegate(receivedData: [String : Any]) {
        //when the httpRequest class receives the data, it will call this call this function and send the info to the delegate(s) class as parameters.
        data = receivedData
    }
}

@ebby94是的,你可以,你可以等待完成处理程序中解锁的信号量,然后你可以返回结果(也在完成处理程序中设置)@Alexander不知道。我会调查的。感谢您提供的信息:)@Ra'salGhul
loginPostRequest
的completionHandler有什么问题?这种方法也是很好的,为什么您希望return@Alexander我有点困惑。你是说我可以从一个异步函数返回一个值吗?我知道我们可以在完成处理程序中获取值,但我不知道我们可以返回值。我试着搜索你说的话,但找不到相关的东西。任何关于您的意思的链接或解释都将不胜感激。@ebby94否,您使用完成处理程序分配给一个局部变量(由完成处理程序闭包捕获,但在同步函数的作用域中定义),然后在异步调用完成后(如信号量的信号所示)首先返回该变量,如果可能,使用同步方法是一个不好的建议。相反,尽可能多地使用异步方法。等待/轮询(阻止当前线程)比异步代码(通常调度到后台线程)更昂贵/效率更低。您正在谈论的
URLSession
的同步方法是什么?我什么都不知道。@vadian我的意思是,如果你的目标是拥有同步行为,那么就使用同步方法。关于URLSession,我错了。这个目标通常是缺乏异步数据处理知识/理解的借口您好,请务必解释您的代码片段。为什么这样做有效?