【问题标题】:How to abort an Ajax request from an Observable?如何中止来自 Observable 的 Ajax 请求?
【发布时间】:2016-12-01 19:19:27
【问题描述】:

我的代码包含这个简单的函数,用于将文件上传到我的 PHP 服务器(xhr 请求嵌套在 RxJS/Observable 中):

fileUpload(file: File): Observable<any> {
    return new Observable( observer => {
        let xhr:XMLHttpRequest = new XMLHttpRequest();
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    observer.next(<any>JSON.parse(xhr.response));
                } else {
                    observer.error(xhr.response);
                    observer.complete();
                }
            }
        };

        xhr.open('POST', '__BACKEND__?action=file-upload', true);
        var formData = new FormData();
        formData.append('file', file, file.name);
        xhr.send(formData);
    });
}

它功能齐全,但现在我还想为其添加某种取消机制。

仅仅取消订阅 Observable 是行不通的,因为我需要以某种方式调用 xhr.abort(),否则我会浪费宝贵的资源进行大量上传。

是否有可能通过修改此代码来获得一个优雅的解决方案,还是我做错了,因为我为此任务使用了RxJS/Observable

【问题讨论】:

    标签: javascript ajax typescript rxjs observable


    【解决方案1】:

    创建Observable 时,您可以通过从构建器函数返回Subscriptionfunction 来指定取消订阅行为:

    fileUpload(file: File): Observable<any> {
        return new Observable( observer => {
            let xhr:XMLHttpRequest = new XMLHttpRequest();
            xhr.onreadystatechange = () => {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                        observer.next(<any>JSON.parse(xhr.response));
                        observer.complete();
                    } else {
                        observer.error(xhr.response);
                    }
                }
            };
    
            xhr.open('POST', '__BACKEND__?action=file-upload', true);
            var formData = new FormData();
            formData.append('file', file, file.name);
            xhr.send(formData);
    
            //Return the tear down logic. 
            //You may also want to check here that it has not already completed
            //Since this gets called in all cases when the `Subscription` terminates
            return () => xhr.abort();
        });
    }
    

    【讨论】:

    • 这就是我要找的答案,谢谢! :) 这是否也适用于热门的 Observables?
    • 你的意思是,unsubscribe 逻辑总是运行吗?是的。
    【解决方案2】:

    返回 xhr 对象并在另一个 observable 中对其执行 abort。

    var uploadObservable = fileUpload();
    var uploadRequest;
    
    uploadObservable.subscribe(
      function (x) {
        uploadRequest = x;
      },
      function (err) {
        console.log('Error: %s', err);
      },
      function () {
        console.log('Completed');
      });
    
    
    var cancelBtn = Rx.Observable.fromEvent(cancelBtn, 'click');
    
    cancelBtn.subscribe(
      function (x) {
        uploadRequest.abort();
      },
      function (err) {
        console.log('Error: %s', err);
      },
      function () {
        console.log('Completed');
      });
    

    或者

     fileUpload()
        .flatMap(function(xhr) {
          Rx.Observable.fromEvent(cancelBtn, 'click').subscribe(function() {xhr.abort()})
        })
    .subscribe(...);
    

    【讨论】:

    • 感谢您的回答,尽管它并不完全是一个优雅的回答:) 公开 xhr 对象是一种糟糕的设计实践。从 builder 函数内部也没有优雅的方法来获取 xhr 对象。
    • 建议您先尝试一下,您的替代代码将无法运行,您需要将subscribe 块更改为finally
    • 我也认为使用 Disposable/Subscription 是正确的方法。但是,您仍然需要保存一些状态/变量。在不保存任何状态/变量的情况下有一个解决方案会很好。以某种方式组合流。 @paulpdaniels 我检查了我的版本,它应该可以工作。为什么我需要 finally 块?
    • 假设您定义了示例,您的第一个示例将运行。第二个示例不会因为您从flatMap 返回一个Subscription 对象,它不是 受支持的类型。为什么需要外部状态?
    【解决方案3】:

    请求将中止到取消订阅调用。 有关更多详细信息,请参阅 AjaxObservable.unsubscribe 源: https://github.com/ReactiveX/rxjs/blob/441d52208df8b9247b01f8ca3993e3a7b0870b10/src/internal/observable/dom/AjaxObservable.ts#L423

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-30
      • 1970-01-01
      • 2013-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多