如何在循环中链接Swift中的promise?

时间:2016-06-20 00:14:04

标签: swift asynchronous promise promisekit

我正在构建一个基于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应用程序中)的含义。理想的情况是,如果有一个构造,通过链接承诺更自然地做到这一点。

1 个答案:

答案 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
    }
}