如何在循环中链接 Swift 中的 Promise?
Posted
技术标签:
【中文标题】如何在循环中链接 Swift 中的 Promise?【英文标题】:How can I chain promises in Swift inside a loop? 【发布时间】:2016-10-21 03:44:43 【问题描述】:我正在构建一个基于 Swift 的 ios 应用程序,该应用程序使用 PromiseKit 来处理 Promise(尽管如果它使我的问题更容易解决,我愿意切换 Promise 库)。有一段代码旨在处理有关覆盖文件的问题。
我的代码大致如下:
let fileList = [list, of, files, could, be, any, length, ...]
for file in fileList
if(fileAlreadyExists)
let overwrite = Promise<Bool> fulfill, reject in
let alert = UIAlertController(message: "Overwrite the file?")
alert.addAction(UIAlertAction(title: "Yes", handler: action in
fulfill(true)
alert.addAction(UIAlertAction(title: "No", handler: action in
fulfill(false)
else
fulfill(true)
overwrite.then result -> Promise<Void> in
Promise<Void> fulfill, reject in
if(result)
// Overwrite the file
else
// Don't overwrite the file
但是,这并没有达到预期的效果; for 循环“完成”的速度与迭代列表的速度一样快,这意味着 UIAlertController 在尝试将一个问题覆盖在另一个问题上时会感到困惑。我想要的是链接承诺,以便只有在用户选择“是”或“否”(以及随后的“覆盖”或“不覆盖”代码已执行)后,for 的下一次迭代循环发生。本质上,我希望整个序列是连续的。
考虑到数组的长度不确定,我如何链接这些承诺?我觉得好像我错过了一些明显的东西。
编辑:以下答案之一建议递归。这听起来很合理,虽然我不确定如果列表变长对 Swift 的堆栈(这是在 iOS 应用程序内部)的影响。理想的情况是,如果有一个结构可以通过链接到 Promise 来更自然地做到这一点。
【问题讨论】:
这是一个类似问题的答案:***.com/a/60942269/3900270 【参考方案1】:一种方法:创建一个获取剩余对象列表的函数。将其用作then
中的回调。在伪代码中:
function promptOverwrite(objects)
if (objects is empty)
return
let overwrite = [...] // same as your code
overwrite.then
do positive or negative action
// Recur on the rest of the objects
promptOverwrite(objects[1:])
现在,我们可能也有兴趣在不使用递归的情况下执行此操作,只是为了避免在我们有数以万计的 Promise 时破坏调用堆栈。 (假设 Promise 不需要用户交互,并且它们都在几毫秒的数量级上解析,因此场景是现实的)。
首先请注意,then
中的回调发生在闭包的上下文中,因此它不能与任何外部控制流进行交互,正如预期的那样。如果我们不想使用递归,我们可能不得不利用其他一些原生特性。
您首先使用 Promise 的原因大概是您(明智地)不想阻塞主线程。然后考虑分拆第二个线程,其唯一目的是编排这些承诺。如果您的库允许显式等待一个承诺,只需执行类似的操作
function promptOverwrite(objects)
spawn an NSThread with target _promptOverwriteInternal(objects)
function _promptOverwriteInternal(objects)
for obj in objects
let overwrite = [...] // same as your code
overwrite.then(...) // same as your code
overwrite.awaitCompletion()
如果你的 promises 库不允许你这样做,你可以通过使用锁来解决它:
function _promptOverwriteInternal(objects)
semaphore = createSemaphore(0)
for obj in objects
let overwrite = [...] // same as your code
overwrite.then(...) // same as your code
overwrite.always
semaphore.release(1)
semaphore.acquire(1) // wait for completion
【讨论】:
为伪代码道歉——我不懂 Swift。但这是一个很好的问题,我认为解决方案与语言无关。 基本上,我认为您建议使用递归。如果列表很短,这是一个合理的想法。如果它变得更长,我有点担心 Swift 的影响(我也不够 Swift 专家知道这是否会产生严重的影响)。 @AndrewFerrier:是的,它是递归的。但我猜测 UIAlertController 会向用户发出提示。您肯定不是建议连续显示数千条提示,对吧? (澄清一下,也许:这里的递归不会影响性能。递归可能会产生影响的唯一原因是如果你破坏了调用堆栈,而调用堆栈通常处理数千个调用. 即使你因为框架和 GUI 事件有大约 100 次调用,并且每个递归调用由于 Promise 而触发了几帧,这仍然意味着你需要数百个提示来破坏堆栈。那时你有一个严重的用户体验问题!:) ) 好点 :) 现在有点理论,但我仍然想了解如果没有 UI 交互会发生这种情况。以上是关于如何在循环中链接 Swift 中的 Promise?的主要内容,如果未能解决你的问题,请参考以下文章
链接多个 Promise,使用 map 循环数组并创建 mongoose 文档?
如何使用 PromiseKit 在 Swift 3 中手动触发 Promise
与 Promise.all() 中的解析相比,为啥在 while 循环中单独解析 Promise 数组时解析更慢? [复制]