What is the expected behaviour Cancelling async request in Redux-observable using takeUntil

只愿长相守 提交于 2019-12-08 03:50:51

问题


i am new to RXJS, i found Redux-observable canceling async request using takeUntil is very useful. but while i was testing it i found that the actual request is still going on even though we cancel the request..

i have this JSbin code snippet to test.

https://jsbin.com/hujosafocu/1/edit?html,js,output

here the actual request is not canceling, even if you cancel the request by clicking the cancel (multiple times) button.

i am not sure this is how it should be.. if yes, then what does it meant by canceling async request. I am bit confused.. Please share some thoughts..

any respond to this will greatly appreciate.. thanks


回答1:


The issue is very subtle, but obviously important. Given your code:

const fetchUserEpic = action$ =>
  action$.ofType(FETCH_USER)
    .delay(2000) // <-- while we're waiting, there is nothing to cancel!
    .mergeMap(action =>
      Observable.fromPromise(
        jQuery.getJSON('//api.github.com/users/redux-observable', data => {
          alert(JSON.stringify(data));
        })
      )
      .map(fetchUserFulfilled)
      .takeUntil(action$.ofType(FETCH_USER_CANCELLED))
    );

The kicker is the .delay(2000). What this is saying is, "don't emit the action to the rest of the chain until after 2000ms". Because your .takeUntil(action$.ofType(FETCH_USER_CANCELLED)) cancellation logic is inside the mergeMap's projection function, it is not yet listening for FETCH_USER_CANCELLED because there is nothing to cancel yet!

If you really want to introduce an arbitrary delay before you make the ajax call, but cancel both the delay OR the pending ajax (if it reaches there) you can use Observable.timer()

const fetchUserEpic = action$ =>
  action$.ofType(FETCH_USER)
    .mergeMap(action =>
      Observable.timer(2000)
        .mergeMap(() =>
          Observable.fromPromise(
            jQuery.getJSON('//api.github.com/users/redux-observable', data => {
              alert(JSON.stringify(data));
            })
          )
          .map(fetchUserFulfilled)
        )
        .takeUntil(action$.ofType(FETCH_USER_CANCELLED))
    );

I imagine you don't really want to introduce the arbitrary delay before your ajax calls in real-world apps, in which case this problem won't exist and the example in the docs is a good starting reference.


Another thing to note is that even without the delay or timer, cancelling the ajax request from your code doesn't cancel the real underlying XMLHttpRequest--it just ignores the response. This is because Promises are not cancellable.

Instead, I would highly recommend using RxJS's AjaxObservable, which is cancellable:

Observable.ajax.getJSON('//api.github.com/users/redux-observable')

This can be imported in several ways. If you're already importing all of RxJS a la import 'rxjs';, it's available as expected. Otherwise, there are several other ways:

import { ajax } from 'rxjs/observable/dom/ajax';

ajax.getJSON('/path/to/thing');

// or

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/dom/ajax';

Observable.ajax.getJSON('/path/to/thing');

It's important to remember, like all the Observable factories, Observable.ajax is lazy meaning it does not make the AJAX request until someone subscribes to it! Where as jQuery.getJSON makes it right away.

So you can put it together like this:

const fetchUserEpic = action$ =>
  action$.ofType(FETCH_USER)
    .mergeMap(action =>
      Observable.timer(2000)
        .mergeMap(() =>
          Observable.ajax.getJSON('//api.github.com/users/redux-observable')
            .do(data => alert(JSON.stringify(data)))
            .map(fetchUserFulfilled)
        )
        .takeUntil(action$.ofType(FETCH_USER_CANCELLED))
    );

A working demo of this can be found here: https://jsbin.com/podoke/edit?js,output




回答2:


This may help someone in future..

Like jayphelps mentioned above the better solution is using RXjs AjaxObservable, because it is canceling the actual XMLHttpRequest rather than neglecting the responds.

But currently there is some issues going on RxJS v5 "RxJS Observable.ajax cross domain issue"

good solution i found is "allows bypassing default configurations" like below:

     const fetchUserEpic = action$ =>
              action$.ofType(FETCH_USER)
                .mergeMap(action =>
                  Observable.timer(2000)
                    .mergeMap(() =>
                        Observable.ajax({
                            url:`//api.github.com/users/redux-observable`,
                            crossDomain: true
                        })
                        .do(data => alert(JSON.stringify(data)))
                        .map(fetchUserFulfilled)
                    )
                    .takeUntil(action$.ofType(FETCH_USER_CANCELLED))
                );

https://github.com/ReactiveX/rxjs/issues/1732



来源:https://stackoverflow.com/questions/40757112/what-is-the-expected-behaviour-cancelling-async-request-in-redux-observable-usin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!