2

我正在设置一个应用程序,promiseKit用作订购异步任务的一种方式。我目前有一个设置,可确保按顺序完成两个异步函数(称为promises)(我们称它们为 1 和 2),并且按顺序完成另一组函数(3 和 4)。大致:

import PromiseKit
override func viewDidAppear(_ animated: Bool) {


        firstly{
            self.promiseOne() //promise #1 happening first (in relation to # 1 and #2)
            }.then{_ -> Promise<[String]> in
                self.promiseTwo()//promise #2 starting after 1 has completed
            }
            .catch{ error in
                print(error)
        }
        firstly{
            self.promiseThree()//Promise #3 happening first (in relation to #3 and #4)
            }.then{_ -> Promise<[String]> in
                self.promiseFour()//Promise #4 starting after #3 has completed
            }.
            .catch{ error in
                print(error)
        }
}

每个firstly都通过确保第一个在第二个可以启动之前完成来确保其中的功能的顺序。使用两个单独firstly的 ' 确保 1 在 2 之前完成,3 在 4 之前完成,并且(重要的是)1 和 3 大致在同一时间开始(在 开始时viewDidAppear())。这是故意这样做的,因为 1 和 3 彼此不相关,并且可以同时启动而没有任何问题(2 和 4 也是如此)。问题是有第五个promise,我们称之为promiseFive必须2 和4 完成运行。我可以链接一个firstly确保顺序是 1,2,3,4,5,但由于 1/2 和 3/4 的顺序不相关,以这种方式链接它们会浪费时间。我不确定如何设置它,以便promiseFive仅在完成 ​​2 和 4 时运行。我曾想过在 2 和 4 结束时调用经过布尔检查的函数,确保另一个firstly已完成然后调用promiseFive但是,由于它们是异步开始的(1/2 和 3/4),因此promiseFive使用这种方法可能会同时被两者调用,这显然会产生问题。解决此问题的最佳方法是什么?

4

2 回答 2

2

在多个其他承诺完成后,您可以使用whenjoin开始某些事情。不同之处在于他们如何处理失败的承诺。听起来你想加入。这是一个具体但简单的示例。

第一个代码块是一个示例,说明如何创建 2 个 Promise 链,然后等待它们都完成,然后再开始下一个任务。正在完成的实际工作被抽象为一些功能。关注这段代码,因为它包含您需要的所有概念信息。

片段

let chain1 = firstly(execute: { () -> (Promise<String>, Promise<String>) in
    let secondPieceOfInformation = "otherInfo" // This static data is for demonstration only

    // Pass 2 promises, now the next `then` block will be called when both are fulfilled
    // Promise initialized with values are already fulfilled, so the effect is identical
    // to just returning the single promise, you can do a tuple of up to 5 promises/values
    return (fetchUserData(), Promise(value: secondPieceOfInformation))

}).then { (result: String, secondResult: String) -> Promise<String> in
    self.fetchUpdatedUserImage()
}

let chain2 = firstly {
    fetchNewsFeed() //This promise returns an array

}.then { (result: [String : Any]) -> Promise<String> in

    for (key, value) in result {
        print("\(key) \(value)")
    }
    // now `result` is a collection
    return self.fetchFeedItemHeroImages()
}

join(chain1, chain2).always {
    // You can use `always` if you don't care about the earlier values

    let methodFinish = Date()
    let executionTime = methodFinish.timeIntervalSince(self.methodStart)
    print(String(format: "All promises finished %.2f seconds later", executionTime))
}

PromiseKit 使用闭包来提供它的 API。闭包有一个范围,就像一个if语句。如果您在语句的范围内定义一个值if,那么您将无法在该范围之外访问它。

您有几个选项可以将多条数据传递到下一个then块。

  1. 使用与所有 Promise 共享范围的变量(您可能希望避免这种情况,因为它在管理异步数据传播流时对您不利)
  2. 使用自定义数据类型来保存两个(或更多)值。这可以是元组、结构、类或枚举。
  3. 使用集合(例如字典),例如chain2
  4. 返回一个承诺元组,示例包含在chain1

在选择方法时,您需要做出最佳判断。

完整代码

import UIKit
import PromiseKit

class ViewController: UIViewController {

    let methodStart = Date()

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        <<Insert The Other Code Snippet Here To Complete The Code>>

        // I'll also mention that `join` is being deprecated in PromiseKit
        // It provides `when(resolved:)`, which acts just like `join` and
        // `when(fulfilled:)` which fails as soon as any of the promises fail
        when(resolved: chain1, chain2).then { (results) -> Promise<String> in
            for case .fulfilled(let value) in results {
                // These promises succeeded, and the values will be what is return from
                // the last promises in chain1 and chain2
                print("Promise value is: \(value)")
            }

            for case .rejected(let error) in results {
                // These promises failed
                print("Promise value is: \(error)")
            }

            return Promise(value: "finished")
            }.catch { error in
                // With the caveat that `when` never rejects
        }
    }

    func fetchUserData() -> Promise<String> {
        let promise = Promise<String> { (fulfill, reject) in

            // These dispatch queue delays are standins for your long-running asynchronous tasks
            // They might be network calls, or batch file processing, etc
            // So, they're just here to provide a concise, illustrative, working example
            DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) {
                let methodFinish = Date()
                let executionTime = methodFinish.timeIntervalSince(self.methodStart)

                print(String(format: "promise1 %.2f seconds later", executionTime))
                fulfill("promise1")
            }
        }

        return promise
    }

    func fetchUpdatedUserImage() -> Promise<String> {
        let promise = Promise<String> { (fulfill, reject) in
            DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) {
                let methodFinish = Date()
                let executionTime = methodFinish.timeIntervalSince(self.methodStart)

                print(String(format: "promise2 %.2f seconds later", executionTime))
                fulfill("promise2")
            }
        }

        return promise
    }

    func fetchNewsFeed() -> Promise<[String : Any]> {
        let promise = Promise<[String : Any]> { (fulfill, reject) in
            DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
                let methodFinish = Date()
                let executionTime = methodFinish.timeIntervalSince(self.methodStart)

                print(String(format: "promise3 %.2f seconds later", executionTime))
                fulfill(["key1" : Date(),
                         "array" : ["my", "array"]])
            }
        }

        return promise
    }

    func fetchFeedItemHeroImages() -> Promise<String> {
        let promise = Promise<String> { (fulfill, reject) in
            DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) {
                let methodFinish = Date()
                let executionTime = methodFinish.timeIntervalSince(self.methodStart)

                print(String(format: "promise4 %.2f seconds later", executionTime))
                fulfill("promise4")
            }
        }

        return promise
    }
}

输出

promise3 1.05 秒后
array ["my", "array"]
key1 2017-07-18 13:52:06 +0000
promise1 2.04 秒后
promise4 3.22 秒后
promise2 4.04 秒后
所有 promises 完成 4.04 秒后
Promise 值为:promise2
承诺值是:promise4

于 2017-07-18T00:07:35.503 回答
1

细节取决于这些各种承诺的类型,但您基本上可以将 1 后跟 2 的承诺作为一个承诺返回,并将 3 后跟 4 的承诺作为另一个返回,然后使用when来运行这两个序列的承诺同时相对于彼此,但仍然享受这些序列中的连续行为。例如:

let firstTwo = promiseOne().then { something1 in
    self.promiseTwo(something1)
}

let secondTwo = promiseThree().then { something2 in
    self.promiseFour(something2)
}

when(fulfilled: [firstTwo, secondTwo]).then { results in
    os_log("all done: %@", results)
}.catch { error in
    os_log("some error: %@", error.localizedDescription)
}

在这种情况下,您试图保持问题相当笼统可能会使您更难看到如何在您的案例中应用此答案。因此,如果您遇到问题,您可能希望更具体地了解这四个 Promise 正在做什么以及它们相互传递的内容(因为这种将结果从一个传递到另一个是 Promise 的优雅特性之一)。

于 2017-07-18T02:18:46.123 回答