使用 Promises 的代码根本不执行
Code using Promises doesn't execute at all
为了这个我花了大半天的时间用头撞墙,我想我终于明白了。这是我今天早上开始寻找时希望存在的问题。
作为背景,我有多年使用 C++ 和 Python 的经验,最近我开始学习 Swift 非 iOS 开发。我将在此处展示的所有内容在 MacBook Pro 上的表现似乎与在我的 Ubuntu PC 上的表现相同。我正在 运行ning Swift 5.4,在命令行中编译和 运行 Swift 包管理器。
我已经通读了 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://whosebug.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 还没有 运行ning。如何让它们真正执行?
这里的问题似乎是所有在线文章都专注于 iOS 开发,并没有真正解决 desktop/server 可执行文件中的 Promise。简单的脚本旨在终止,而移动应用程序旨在无限期地 运行。
如果你深入研究 Thenable
代码,你会发现 .then
、.done
、.catch
等都有一个 [=16] 的参数=],深入挖掘,默认为 DispatchQueue.main
。因为这些示例中的顶级代码正在使用主线程,所以主 DispatchQueue
永远没有机会执行分配给它的工作。通过在最后调用 dispatchMain()
将主线程交给调度系统,这两个示例都可以很容易地执行 Promise 链。
但是,dispatchMain()
永远不会 returns,因此对于旨在 运行 完成的可执行文件来说,这不是一个好的选择。更好的解决方案是指定一个备用队列来执行这些承诺中的每一个。对于单个任务,我们可以在 .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://whosebug.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
处理程序中做尽可能少的工作,并释放你的主线程以便它可以自旋更快地启动其他线程上的任务。
为了这个我花了大半天的时间用头撞墙,我想我终于明白了。这是我今天早上开始寻找时希望存在的问题。
作为背景,我有多年使用 C++ 和 Python 的经验,最近我开始学习 Swift 非 iOS 开发。我将在此处展示的所有内容在 MacBook Pro 上的表现似乎与在我的 Ubuntu PC 上的表现相同。我正在 运行ning Swift 5.4,在命令行中编译和 运行 Swift 包管理器。
我已经通读了 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://whosebug.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 还没有 运行ning。如何让它们真正执行?
这里的问题似乎是所有在线文章都专注于 iOS 开发,并没有真正解决 desktop/server 可执行文件中的 Promise。简单的脚本旨在终止,而移动应用程序旨在无限期地 运行。
如果你深入研究 Thenable
代码,你会发现 .then
、.done
、.catch
等都有一个 [=16] 的参数=],深入挖掘,默认为 DispatchQueue.main
。因为这些示例中的顶级代码正在使用主线程,所以主 DispatchQueue
永远没有机会执行分配给它的工作。通过在最后调用 dispatchMain()
将主线程交给调度系统,这两个示例都可以很容易地执行 Promise 链。
但是,dispatchMain()
永远不会 returns,因此对于旨在 运行 完成的可执行文件来说,这不是一个好的选择。更好的解决方案是指定一个备用队列来执行这些承诺中的每一个。对于单个任务,我们可以在 .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://whosebug.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
处理程序中做尽可能少的工作,并释放你的主线程以便它可以自旋更快地启动其他线程上的任务。