草庐IT

swift - 如何保证一起 'start'的两个异步任务在运行另一个之前完成?

coder 2023-09-14 原文

我正在设置一个应用程序,它利用 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 通过确保第一个函数在第二个函数启动之前完成来确保其中函数的顺序。使用两个单独的 first 确保 1 在 2 之前完成,3 在 4 之前完成,(重要的是)1 和 3 大致同时开始(在viewDidAppear() 的开始)。这是有意为之的,因为 1 和 3 彼此不相关,可以同时启动而不会出现任何问题(2 和 4 也是如此)。问题是有第五个 promise ,我们称之为 promiseFive,它必须 2 和 4 完成后运行。我可以先链接一个确保顺序为 1、2、3、4、5 的 ,但由于 1/2 和 3/4 的顺序不相关,因此以这种方式链接它们会浪费时间。 我不确定如何设置它,以便 promiseFive 仅在 2 和 4 都完成后运行。我曾想过在 2 和 4 的末尾调用 bool 检查函数,使得确保其他 firSTLy 已完成然后调用 promiseFive 但是,由于它们开始 异步(1/2 和 3/4),因此有可能使用这种方法,promiseFive 将同时被两者调用,这显然会产生问题。解决此问题的最佳方法是什么?

最佳答案

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

第一个代码块是一个示例,说明如何创建 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 block 。

  1. 使用与所有 promise 共享范围的变量(您可能希望避免这种情况,因为它在管理异步数据传播流时对您不利)
  2. 使用自定义数据类型来保存两个(或更多)值。这可以是元组、结构、类或枚举。
  3. 使用集合(如字典),例子在chain2
  4. 返回一个 promise 元组,示例包含在 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 seconds later
array ["my", "array"]
key1 2017-07-18 13:52:06 +0000
promise1 2.04 seconds later
promise4 3.22 seconds later
promise2 4.04 seconds later
All promises finished 4.04 seconds later
Promise value is: promise2
Promise value is: promise4

关于swift - 如何保证一起 'start'的两个异步任务在运行另一个之前完成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45155096/

有关swift - 如何保证一起 'start'的两个异步任务在运行另一个之前完成?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  6. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  7. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  8. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  9. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  10. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

随机推荐