jjzjj

javascript - RxJs flatMapLatest/switchMap 取消回调。 onCancel() 在哪里?

coder 2024-05-12 原文

我有 2 个嵌套的 Observable Streams,它们执行 HTTP 请求。现在我想显示加载指示器,但无法使其正常工作。

var pageStream = Rx.createObservableFunction(_self, 'nextPage')
        .startWith(1)
        .do(function(pageNumber) {
            pendingRequests++;
        })
        .concatMap(function(pageNumber) {
            return MyHTTPService.getPage(pageNumber);
        })
        .do(function(response) {
            pendingRequests--;
        });

Rx.createObservableFunction(_self, 'search')
        .flatMapLatest(function(e) {
            return pageStream;
        })
        .subscribe();



search();
nextPage(2);
nextPage(3);
search();

这将触发 pendingRequests++ 4 次,但 pendingRequests-- 只会触发一次,因为 flatMapLatest 会在前 3 个 HTTP 之前取消内部 observable响应到达。

我找不到类似 onCancel 回调的任何东西。我还尝试了 onCompletedonError,但它们也不会被 flatMapLatest 触发。

还有其他方法可以实现吗?

谢谢!

编辑:所需的加载指示器行为

  1. 示例:单个 search() 调用。

    • search() -> 开始加载指示器
    • 当 search() 响应返回时 -> 禁用加载指示器
  2. 示例:search()nextPage() 调用。 (在 search() 响应返回之前调用了 nextPage()。)

    • search() -> 开始加载指示器
    • nextPage() -> 指示器已经启动,但此处无需执行任何操作
    • 两个响应到达后停止加载指示器
  3. 示例:search()search()(search() 调用相互覆盖,尽管第一个调用的响应可以被忽略)

    • search() -> 开始加载指示器
    • search() -> 指示器已经启动,但这里无需执行任何操作
    • second search() 的响应到达时停止加载指示器
  4. 示例:search()nextPage()search()(再说一遍:因为第二次search(),前面search()和nextPage()的响应可以忽略)

    • search() -> 开始加载指示器
    • nextPage() -> 指示器已经启动,但此处无需执行任何操作
    • search() -> 指示器已经启动,但这里无需执行任何操作
    • second search() 的响应到达时停止加载指示器
  5. 示例:search()nextPage()。但是这次 nextPage() 是在 search() 响应返回后调用的。

    • search() -> 开始加载指示器
    • 停止加载指示器,因为 search() 响应到达
    • nextPage() -> 开始加载指示器
    • 停止加载指示器,因为 nextPage() 响应到达

我尝试使用pendingRequests 计数器,因为我可以同时有多个相关 请求(例如:search()、nextPage()、nextPage ())。然后,当然我想在所有相关请求完成后禁用加载指示器。

调用search()、search()时,第一个search()是无关紧要的。这同样适用于 search()、nextPage()、search()。在这两种情况下,只有一个事件的相关请求(最后一个 search())。

最佳答案

使用 switchMap 又名 flatMapLatest,您希望在新的外部项到达时尽快 trim 当前内部流的执行。这无疑是一个很好的设计决定,否则它会带来很多困惑并允许一些诡异的 Action 。如果你真的想做一些事情 onCancel 你总是可以使用自定义 unsubscribe 回调创建你自己的可观察对象。但我仍然建议不要将 unsubscribe 与外部上下文的状态更改结合起来。理想情况下,unsubscribe 只会清理内部使用的资源。

然而,您的特殊情况可以在不访问 onCancel 或类似方法的情况下得到解决。关键观察是 - 如果我正确理解您的用例 - 在 search 上所有以前/未决的操作可能会被忽略。因此,不用担心递减计数器,我们可以简单地从 1 开始计数。

关于片段的一些评论:

  • 使用 BehaviorSubject 来计算未决请求 - 因为它已准备好与其他流组合;
  • 检查了您在问题中发布的所有案例,它们都有效;
  • 添加了一些模糊测试来证明正确性;
  • 不确定当 search 仍未决时是否允许 nextPage - 但似乎只是使用 concatMapTo 的问题vs merge;
  • 仅使用标准的Rx 运算符。

PLNKR

console.clear();

const searchSub = new Rx.Subject(); // trigger search 
const nextPageSub = new Rx.Subject(); // triger nextPage
const pendingSub = new Rx.BehaviorSubject(); // counts number of pending requests

const randDurationFactory = min => max => () => Math.random() * (max - min) + min;
const randDuration = randDurationFactory(250)(750);
const addToPending = n => () => pendingSub.next(pendingSub.value + n);
const inc = addToPending(1);
const dec = addToPending(-1);

const fakeSearch = (x) => Rx.Observable.of(x)
  .do(() => console.log(`SEARCH-START: ${x}`))
  .flatMap(() => 
    Rx.Observable.timer(randDuration())
    .do(() => console.log(`SEARCH-SUCCESS: ${x}`)))

const fakeNextPage = (x) => Rx.Observable.of(x)
  .do(() => console.log(`NEXT-PAGE-START: ${x}`))
  .flatMap(() =>
    Rx.Observable.timer(randDuration())
    .do(() => console.log(`NEXT-PAGE-SUCCESS: ${x}`)))

// subscribes
searchSub
  .do(() => console.warn('NEW_SEARCH'))
  .do(() => pendingSub.next(1)) // new search -- ingore current state
  .switchMap(
    (x) => fakeSearch(x)
    .do(dec) // search ended
    .concatMapTo(nextPageSub // if you wanted to block nextPage when search still pending
      // .merge(nextPageSub // if you wanted to allow nextPage when search still pending
      .do(inc) // nexpage started
      .flatMap(fakeNextPage) // optionally switchMap
      .do(dec) // nextpage ended
    )
  ).subscribe();

pendingSub
  .filter(x => x !== undefined) // behavior-value initially not defined
  .subscribe(n => console.log('PENDING-REQUESTS', n))

// TEST
const test = () => {
    searchSub.next('s1');
    nextPageSub.next('p1');
    nextPageSub.next('p2');

    setTimeout(() => searchSub.next('s2'), 200)
  }
// test();

// FUZZY-TEST
const COUNTER_MAX = 50;
const randInterval = randDurationFactory(10)(350);
let counter = 0;
const fuzzyTest = () => {
  if (counter % 10 === 0) {
    searchSub.next('s' + counter++)
  }
  nextPageSub.next('p' + counter++);
  if (counter < COUNTER_MAX) setTimeout(fuzzyTest, randInterval());
}

fuzzyTest()
<script src="https://npmcdn.com/rxjs@5.0.0-beta.11/bundles/Rx.umd.js"></script>

关于javascript - RxJs flatMapLatest/switchMap 取消回调。 onCancel() 在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39236361/

有关javascript - RxJs flatMapLatest/switchMap 取消回调。 onCancel() 在哪里?的更多相关文章

  1. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  2. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  3. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  4. ruby-on-rails - ActiveRecord::Associations::CollectionProxy 从哪里获取.each 实例方法? - 2

    假设我有模型Topics和Posts,其中Topichas_many:posts和Postbelongs_to:topic。此时我的数据库中已经有了一些东西。如果我进入Rails控制台并输入Topic.find(1).posts我相信我得到了一个CollectionProxy对象。=>#]>我可以对此调用.each以获得枚举器对象。=>#]:each>我对CollectionProxy如何处理.each感到困惑。我意识到它在某些时候是继承的,但我一直在阅读API文档,他们并没有说得很清楚CollectionProxy是从什么继承的,除非我遗漏了一些明显的东西。Thispage似乎并没有

  5. ruby-on-rails - 将保存回调添加到单个 ActiveRecord 实例,可以吗? - 2

    是否可以为单个ActiveRecord实例添加回调?作为进一步的限制,这是继续使用库,所以我无法控制该类(除了对其进行猴子修补)。这或多或少是我想做的:defdo_something_creazymessage=Message.newmessage.on_save_call:do_even_more_crazy_stuffenddefdo_even_more_crazy_stuff(message)puts"Message#{message}hasbeensaved!Hallelujah!"end 最佳答案 你可以通过在创建对象后立

  6. ruby-on-rails - 闪存消息存储在哪里? - 2

    我以为它们存储在cookie中-但不,检查cookie没有任何结果。session也不存储它们。那么,我在哪里可以找到它们?我需要这个来直接设置它们(而不是通过flashhash)。 最佳答案 它们存储在inyoursessionstore.自rails2.0以来的默认设置是cookie存储,但请检查config/initializers/session_store.rb以检查您是否使用默认设置以外的东西。 关于ruby-on-rails-闪存消息存储在哪里?,我们在StackOverf

  7. ruby-on-rails - Ruby method_added 回调不触发包括模块 - 2

    我想写一点“Deprecate-It”库并经常使用“method_added”回调。但是现在我注意到在包含模块时不会触发此回调。是否有任何回调或变通方法,以便在某些内容包含到自身时通知类“Foobar”?用于演示的小Demo:#IncludingModulswon'ttriggermethod_addedcallbackmoduleInvisibleMethoddefinvisible"Youwon'tgetacallbackfromme"endendclassFoobardefself.method_added(m)puts"InstanceMethod:'#{m}'addedto'

  8. ruby-on-rails - Ruby 如何知道在哪里可以找到所需的文件? - 2

    这里还有一个新手问题:require'tasks/rails'我在每个Rails项目的根路径中的Rakefile中看到了这一行。我猜这行用于要求vendor/rails/railties/lib/tasks/rails.rb加载所有rake任务:$VERBOSE=nil#LoadRailsrakefileextensionsDir["#{File.dirname(__FILE__)}/*.rake"].each{|ext|loadext}#LoadanycustomrakefileextensionsDir["#{RAILS_ROOT}/lib/tasks/**/*.rake"].so

  9. ruby - 在 Mechanize 中使用 JavaScript 单击链接 - 2

    我有这个:AccountSummary我想单击该链接,但在使用link_to时出现错误。我试过:bot.click(page.link_with(:href=>/menu_home/))bot.click(page.link_with(:class=>'top_level_active'))bot.click(page.link_with(:href=>/AccountSummary/))我得到的错误是:NoMethodError:nil:NilClass的未定义方法“[]” 最佳答案 那是一个javascript链接。Mechan

  10. ruby-on-rails - 使用 before_save 回调或自定义验证器添加验证错误? - 2

    我有一个模型Listingbelongs_to:user。或者,Userhas_many:listings。每个列表都有一个对其进行分类的类别字段(狗、猫等)。User还有一个名为is_premium的bool字段。这是我验证类别的方式...validates_format_of:category,:with=>/(dogs|cats|birds|tigers|lions|rhinos)/,:message=>'isincorrect'假设我只想让高级用户能够添加老虎、狮子和犀牛。我该怎么做?最好在before_save方法中执行此操作吗?before_save:premium_che

随机推荐