使用 Promises 的代码根本不执行

Posted

技术标签:

【中文标题】使用 Promises 的代码根本不执行【英文标题】:Code using Promises doesn't execute at all 【发布时间】:2021-06-11 06:32:20 【问题描述】:

我花了一天的大部分时间在墙上撞墙,我想我终于明白了。这是我今天早上开始寻找时希望存在的问题。

作为背景,我有几年使用 C++ 和 Python 的经验,最近我开始学习 Swift 进行非 ios 开发。我将在这里展示的所有内容在 MacBook Pro 上的行为似乎与在我的 Ubuntu PC 上的行为相同。我正在运行 Swift 5.4,在命令行中使用 Swift Package Manager 编译和运行。

我已经阅读了几篇关于在 Swift 中使用 Promises 的文章,但没有加起来。他们展示的示例使您看起来好像只需调用firstly,将几个.then 调用链接在一起,然后将它们与.done.catch 绑定在一起,一切都会正常工作。当我尝试这个时,我大多会得到一堆错误,但有时我很幸运能够编译它,却发现当我运行它时什么都没有发生。

这是一个示例 main.swift 文件,说明了我的困惑。

import PromiseKit

firstly 
    Promise<String>  seal in
        print("Executing closure")
        seal.fulfill("Hello World!")
    
.done  str in
    print(str)

当我运行它时,它只会打印Executing closure,即使我在它之后添加了一个睡眠,或者我在结果上调用.wait()。为什么firstly 块执行但done 块不执行?

这是另一个。我花了一段时间才编译它,但它仍然没有达到我的预期:

import Foundation
import PromiseKit

let url = URL(string: "https://***.com/")!

enum SampleError: Error 
    case noResponse
    case badCode(Int)
    case couldNotDecode


firstly 
    Promise<Data>  seal in
        URLSession.shared.dataTask(with: url)  data, response, error in
            if let error = error 
                return seal.reject(error)
            

            guard let data = data, let response = response as? HTTPURLResponse else 
                return seal.reject(SampleError.noResponse)
            

            guard (200..<300).contains(response.statusCode) else 
                return seal.reject(SampleError.badCode(response.statusCode))
            

            seal.fulfill(data)
        .resume()
    
.then  data -> Promise<String> in
    Promise<String>  seal in
        if let str = String(data: data, encoding: .utf8) 
            seal.fulfill(str)
         else 
            seal.reject(SampleError.couldNotDecode)
        
    
.done  str in
    print(str)
.catch  error in
    print("Error: \(error)")

我希望它打印 Stack Overflow 主页的 html 内容,但它什么也不打印。

似乎我已经完成了所有设置,但是 Promise 没有运行。如何让它们真正执行?

【问题讨论】:

【参考方案1】:

似乎这里的问题是所有在线文章都专注于 iOS 开发,并没有真正解决桌面/服务器可执行文件中的 Promise。简单脚本旨在终止,而移动应用程序旨在无限期运行。

如果您深入研究Thenable 代码,您会发现.then.done.catch 等有一个DispatchQueue 的参数,深入挖掘,默认为@987654328 @。因为这些示例中的***代码正在使用主线程,所以主 DispatchQueue 永远没有机会执行分配给它的工作。通过在最后调用 dispatchMain() 将主线程让给调度系统,这两个示例都可以轻松地执行 Promise 链。

但是,dispatchMain() 永远不会返回,因此对于打算运行完成的可执行文件来说,这不是一个好的选择。更好的解决方案是指定一个备用队列来执行这些承诺中的每一个。对于单个任务,我们可以在 .finally 块中使用信号量来表示所有工作都已完成。对于多项任务,我怀疑您需要使用DispatchGroup

这是第一个示例的更新版本:

import Foundation
import PromiseKit

let queue = DispatchQueue.global()
let semaphore = DispatchSemaphore(value: 0)

firstly 
    Promise<String>  seal in
        print("Executing closure")
        seal.fulfill("Hello World!")
    
.done(on: queue)  str in
    print(str)
.catch(on: queue)  error in
    print("Error: \(error)")
.finally(on: queue) 
    semaphore.signal()


semaphore.wait()

这是第二个的工作版本:

import Foundation
import PromiseKit

let queue = DispatchQueue.global()
let semaphore = DispatchSemaphore(value: 0)

let url = URL(string: "https://***.com/")!

enum SampleError: Error 
    case noResponse
    case badCode(Int)
    case couldNotDecode


firstly 
    Promise<Data>  seal in
        URLSession.shared.dataTask(with: url)  data, response, error in
            if let error = error 
                return seal.reject(error)
            

            guard let data = data, let response = response as? HTTPURLResponse else 
                return seal.reject(SampleError.noResponse)
            

            guard (200..<300).contains(response.statusCode) else 
                return seal.reject(SampleError.badCode(response.statusCode))
            

            seal.fulfill(data)
        .resume()
    
.then(on: queue)  data -> Promise<String> in
    Promise<String>  seal in
        if let str = String(data: data, encoding: .utf8) 
            seal.fulfill(str)
         else 
            seal.reject(SampleError.couldNotDecode)
        
    
.done(on: queue)  str in
    print(str)
.catch(on: queue)  error in
    print("Error: \(error)")
.finally (on: queue) 
    semaphore.signal()


semaphore.wait()

有趣的是,firstly 块似乎在主线程上同步执行。我突然想到,如果你有很多任务要运行,最好在 firstly 处理程序内做尽可能少的工作,并释放你的主线程,以便它可以启动另一个任务线程更快。

【讨论】:

***.com/questions/63425657/… ***.com/questions/34828991/using-gcd-in-swift-cli

以上是关于使用 Promises 的代码根本不执行的主要内容,如果未能解决你的问题,请参考以下文章

我在spring设置了事务管理,但是对于需要使用事务的方法,如果不手动开启事务,这个方法根本不执行

Promises/A+规范

如何等待异步函数执行?

使用 Promises 执行流程,将一个函数的输出作为下一个函数的输入传递

在for循环中同步promises?

在循环中对promises感到困惑