【问题标题】:How can I fire off a dynamically created list of http requests in Angular 8?如何在 Angular 8 中触发动态创建的 http 请求列表?
【发布时间】:2019-11-29 16:26:13
【问题描述】:

在我要迁移到 Angular 8 的以前的 AngularJS 应用程序中,我有一个函数,它可以通过绑定函数并将它们放在一个数组中来下载函数,等待使用 reduce 函数调用。例如:

function stageForDownload() {
  $scope.files.forEach(function (file) {
    if (file.checked) {
      $scope.downloadFunctions.push(downloadFile.bind(null, file));
    }
  });
}

function downloadStaged() {
  $scope.downloadFunctions.reduce(
    function (prev, next) {
      return prev.then(next);
    }, Promise.resolve())
    .then( /* do something now that all files are downloaded */ );
  }
}

这段代码基本上会以一种一种方式解决promise,直到函数列表为空。与 Angular 8 一样,promise 的结构以不同的方式工作(这意味着它们现在使用 ECMA 6 实现),我不确定如何迁移此代码。让我更加困惑的是, HttpClient 角度提供的现在返回一个可以订阅的 Observable;虽然 rsjx 的 forkJoin() 方法似乎完全支持我想要做的事情,但它不会接受绑定函数列表。

我只需要知道所有功能何时完成,因为它们是运行导出服务方法来下载文件的空白。所以我不一定需要从这些正在减少的方法中返回/订阅任何数据。

编辑:

还有两个我忘了提及的功能。这里是downloadFile,负责调用exportService。


        function downloadFile(file) {

            var deferred = $q.defer();
            $scope.date = formatDate($scope.datepicker.selectedDate);
            $scope.fileDate = dateToYMD($scope.datepicker.selectedDate);

            exportService.exportData(file.FileNamePrefix + " " + $scope.fileDate + ".xlsx", 'SOME_API_LOCATION' + $scope.date, file).then(
                function () {
                    deferred.resolve();
                },
                function (error) {
                    deferred.reject();
                    notificationService.displayError("Internal Error!");
                });

            return deferred.promise;
        }

这里是 exportService 本身:

(function (app) {

    'uuse strict';

    app.factory('exportService', exportService);

    exportService.$inject = ['$q', '$http'];

    function exportService($q, $http) {

        var service = {
            exportData: exportData,
            createFilename: createFilename
        };

        function exportData(filename, url, data) {

            var config = {
                responseType: 'arraybuffer'
            };

            return $http.post(url, data, config).then(
                function (response) {
                    var deferred = $q.defer();
                    var data = response.data;
                    var status = response.status;
                    var headers = response.headers();
                    var octetStreamMime = 'application/octet-stream';
                    var success = false;
                    var contentType = headers['content-type'] || octetStreamMime;
                    try {
                        // Try using msSaveBlob if supported
                        var blob = new Blob([data], { type: contentType });
                        if (navigator.msSaveBlob)
                            navigator.msSaveBlob(blob, filename);
                        else {
                            // Try using other saveBlob implementations, if available
                            var saveBlob = navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
                            if (saveBlob === undefined) throw "Not supported";
                            saveBlob(blob, filename);
                        }
                        success = true;
                        deferred.resolve();
                    } catch (ex) {
                    }
                    if (!success) {
                        // Get the blob url creator
                        var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
                        if (urlCreator) {
                            // Try to use a download link
                            var link = document.createElement('a');
                            if ('download' in link) {
                                // Try to simulate a click
                                try {
                                    // Prepare a blob URL
                                    var blob = new Blob([data], { type: contentType });
                                    var url = urlCreator.createObjectURL(blob);
                                    link.setAttribute('href', url);
                                    // Set the download attribute (Supported in Chrome 14+ / Firefox 20+)
                                    link.setAttribute("download", filename);
                                    // Simulate clicking the download link
                                    var event = document.createEvent('MouseEvents');
                                    event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
                                    link.dispatchEvent(event);
                                    success = true;
                                    deferred.resolve();
                                } catch (ex) {
                                }
                            }
                            if (!success) {
                                // Fallback to window.location method
                                try {
                                    var blob = new Blob([data], { type: octetStreamMime });
                                    var url = urlCreator.createObjectURL(blob);
                                    window.location = url;
                                    success = true;
                                    deferred.resolve();
                                } catch (ex) {
                                    deferred.reject();
                                }
                            }
                        }
                    }
                    return deferred.promise;
                },
                function (error) {
                    return $q.reject(error);
                });
        }
    }
})(angular.module('app'));

所以看来我的问题不一定是触发一个动态创建的 http 请求列表,而是如何使用 $q 将承诺转换为 ECMA6+ 承诺。

【问题讨论】:

  • angular http.get 返回一个你可以调用.toPromise() 的可观察对象,你可以将它放入一个数组中。然后您可以拨打Promise.all()。触发并等待完成
  • 我在 OP 中添加了更多信息。我相信 exportService 仍然可以使用 subscribe 来处理 http 请求,但是我应该为将 arraybuffer 下载为 xlsx 文件的逻辑做出新的承诺,对吗?

标签: javascript angularjs angular migration angular8


【解决方案1】:

您可以使用fromPromise 变成Observable

const observables: Observable<any>[] = promises.map(promise => from(promise));

之后,您可以释放 RxJS。你可以使用 forkJoin 来获取你所有的 Promise 的 Observable

forkJoin(observables).subscribe(files => { 
  // do things with files
});

forkJoin 的实现方式只有在可观察对象完成后才会触发。在这里不会有问题,但如果您想批量处理未完成的请求,请考虑zip

如果您想使用 HttpClient,您将拥有不同的 observables 来源。

const observables: Observable<any>[] = urls.map(url => this.httpClient.get(url));

forkJoin 是一样的。关键是forkJoin 接受Observables 的数组。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-07
    • 2018-07-23
    相关资源
    最近更新 更多