我正在构建一个基于Swift的iOS应用程序,它使用PromiseKit来处理promises(尽管如果它让我的问题更容易解决,我可以切换到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在尝试将一个问题叠加到另一个问题时会感到困惑。我想要的是链接的承诺,所以只有当用户选择“是”或“否”(并且后续的“覆盖”或“不覆盖”代码已经执行)才会进行下一次迭代循环发生。基本上,我希望整个序列是顺序的。
如果数组的长度不确定,我如何链接这些承诺呢?我觉得我好像缺少了一些明显的东西。
修改:以下答案之一表示递归。这听起来很合理,但如果列表增长很长,我不确定Swift的堆栈(这是在iOS应用程序中)的含义。理想的情况是,如果有一个构造,通过链接承诺更自然地做到这一点。
答案 0 :(得分:0)
一种方法:创建一个获取剩余对象列表的函数。将其用作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:])
}
}
现在,我们可能也有兴趣在没有递归的情况下这样做,只是为了避免在我们有成千上万的承诺时吹掉调用堆栈。 (假设promises不需要用户交互,并且它们都会在几毫秒的时间内解析,因此场景是真实的。)
首先请注意,then
中的回调 - 发生在闭包的上下文中,因此它不能像预期的那样与任何外部控制流交互。如果我们不想使用递归,我们可能不得不利用其他一些原生功能。
你首先使用promises的原因可能是你(明智地)不想阻止主线程。然后,考虑剥离第二个线程,其唯一目的是协调这些承诺。如果您的库允许明确等待承诺,只需执行类似
的操作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
}
}