如何确保两个“开始”的异步任务在运行另一个之前完成?

时间:2017-07-17 23:21:05

标签: swift asynchronous promisekit

我正在设置一个使用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也是如此)。问题是存在第五个承诺,让我们称之为<{em>仅 在 2和4之后运行<{1}}。我可以链接一个promiseFive确保订单是1,2,3,4,5,但由于1/2和3/4的顺序无关,以这种方式链接它们会浪费时间。 我不确定如何设置它以便firstly仅在完成2和4时运行。我想在2和4的末尾都有布尔检查函数调用,确保其他promiseFive已完成,然后调用firstly但是,由于它们开始异步(1/2和3/4),因此可能会调用promiseFive两者在同一时间采用这种方法,这显然会产生问题。最好的方法是什么?

2 个答案:

答案 0 :(得分: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使用closures来提供它的API。闭包的范围就像if语句一样。如果您在if语句的范围内定义了一个值,那么您将无法在该范围之外访问该值。

您可以通过多种方式将多个数据传递到下一个then块。

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

    在选择方法时,您需要使用最佳判断。

    完整代码

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

    输出

      承诺3 1.05秒后   数组[&#34;我&#34;,&#34;数组&#34;]
      key1 2017-07-18 13:52:06 +0000
      承诺1 2.04秒后   承诺4 3.22秒后   promise2 4.04秒后   所有承诺在4.04秒后完成   承诺价值是:promise2
      承诺价值是:promise4

答案 1 :(得分:1)

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

when

这种情况可能会导致您保持问题相当通用的尝试可能会让您更难以了解如何在您的案例中应用此答案。所以,如果你磕磕绊绊,你可能想要更具体地说明这四个承诺正在做什么以及它们相互传递什么(因为这种结果从一个传递到另一个是承诺的优雅特征之一)。