【问题标题】:Multiple asynchronous calls in AngularJS using the resultAngularJS 中使用结果的多个异步调用
【发布时间】:2017-06-10 15:45:57
【问题描述】:

在我正在开发的 AngularJS 应用程序中,我尝试异步处理多个上传的文件,以便为将它们添加到 SuiteCRM 实例的例程做好准备。总体而言,代码没有错误,但我发现延迟调用一个接一个地执行,并且在解决所有承诺之前不会将结果传递给等待脚本。也许有人可以帮助我解决如何链接这些以获得我想要的结果?

  function fileReader(file) { // perform async operation
      var deferred = $q.defer();
      var reader = new FileReader();
      reader.onload = function () {
          deferred.resolve(reader.result.replace(/^(.*),/g, ""));
      };
      reader.readAsDataURL(file);
      return deferred.promise;
  }

  $scope.uploadFiles = function () {
        console.log('Starting uploads');
        $scope.uploadClicked = true;
        $scope.uploadProgress = {};
        $applicationID = $('#application_id').val();
        var tmpMerchData = SessionService.getMerchantData();
        var isoID = SessionService.getISOID();
        if ($scope.fileList && $scope.fileList.length) {
            var $myFiles = $scope.fileList;
            for (var f = 0; f < $myFiles.length; f++) {
                var thisFile = $myFiles[f];
                console.debug(thisFile);
                fileReader(thisFile.file).then(function (result) {
                    ApplicationFactory.createNewUploadedDocument(thisFile, thisFile.templatetype, result, thisFile.description, $scope.userID, tmpMerchData.id, $applicationID, isoID).then(
                            function successCallback(response) {
                                if (response.data.status === 1) {
                                    console.log("Document creation succeeded");
                                    $scope.uploadProgress = {
                                        'progress': 100,
                                        'index': f
                                    };
                                } else {
                                    console.log("Document creation failed");
                                }
                            }, function errorCallback(response) {
                        console.debug("Error returned from createNewUploadedDocument", response);
                    });
                });
            }
        }
    };

所以,你看,我需要 FileReader 来处理文件,返回文件的内容,以便将它传递给 $scope.fileList 中每个 f(文件)的 createNewUploadedDocument 函数。 NGF 的多文件选项对我不可用,因为每个文件都有特定的模板类型和描述。

您可以提供或建议的任何帮助将不胜感激。

编辑 - 我已阅读此Chaining multiple promises created through for loop 和此angular $q, How to chain multiple promises within and after a for-loop,但都没有针对我的具体情况提供任何有用的反馈。我知道可以使用 $q.all 嵌套和组合承诺,但它们仍然可以相互提供结果数据吗? 我的内部方法是调用 REST API - 并不是说​​它有任何区别 - 但我确实需要 FileReader 承诺的结果才能调用它。

【问题讨论】:

  • 什么部分不起作用?代码有点乱,但应该可以工作,但是由于您不太清楚您想要的结果是什么,所以很难提供帮助。
  • 在调用底层函数之前,它会遍历所有的 fileReader 承诺。
  • 这是不可能的。当第一个承诺从fileReader 解决时,它应该继续到第一个.then()。没有什么可以阻止它继续,所以在输入任何 .then() 之前它会等待所有承诺的情况是不合逻辑的。
  • 我已经控制了这个输出,它确实做到了——遍历每个fileReader,然后只提供最后一个result.then()。看起来可能不合逻辑(而且我认为代码也应该像编写的那样工作)!

标签: angularjs asynchronous promise filereader deferred


【解决方案1】:

您可以有效地使用 $q 服务。 当你使用 $q.all() 时,你传递了一个 Promise 数组,结果你以相同的顺序获得了每个 Promise 的结果数组。 所以我们可以像这样重写你的代码:

if ($scope.fileList && $scope.fileList.length) {
   var $myFiles = $scope.fileList;
   var promisesFileReader = [];
   var promisesUploadedDocument = [];
   for (var f = 0; f < $myFiles.length; f++) {
      var thisFile = $myFiles[f];
      console.debug(thisFile);
      promisesFileReader.push(fileReader(thisFile.file));
   }

   $q.all(promisesFileReader)
      .then(function(results) {

         //We loop on results and push all the UploadDocument promises with the correct result 
         for (var i = 0; i < results.length; i++) {
            promisesUploadedDocument.push(ApplicationFactory.createNewUploadedDocument(thisFile, thisFile.templatetype, results[i], thisFile.description, $scope.userID, tmpMerchData.id, $applicationID, isoID));
         }

         $q.all(promisesUploadedDocument)
            .then(
               function successCallback(response) {
                  if (response.data.status === 1) {
                     console.log("Document creation succeeded");
                     $scope.uploadProgress = {
                        'progress': 100,
                        'index': f
                     };
                  } else {
                     console.log("Document creation failed");
                  }
            },
              function errorCallback(response) {
                 console.debug("Error returned from createNewUploadedDocument", response);
            });
       });
}

【讨论】:

  • 这不起作用,因为您在 for 循环中的每次迭代都调用了 $q.all。您应该将$q.all 移到循环之外,并在您拥有来自fileReader 的完整承诺数组时调用它一次。
  • 对不起,这不起作用。它返回“结果”f 次数。每次作为一个越来越大的对象。
  • 对不起。您必须将 $q.all 请求排除在 for 循环之外。循环允许将所有 Promise 推送到一个数组中,然后您可以使用 $q.all 调用每个 Promise。
【解决方案2】:

ANSWER - 这个原始代码的更新版本解决了这个问题。

请注意,angular.forEach 已实现以及一些已清理的变量名称和重用。

    $scope.uploadFiles = function () {
        console.log('Starting uploads');
        $scope.uploadClicked = true;
        $scope.uploadProgress = {};
        $applicationID = $('#application_id').val();
        var tmpMerchData = SessionService.getMerchantData();
        var isoID = SessionService.getISOID();
        if ($scope.fileList && $scope.fileList.length) {
            angular.forEach($scope.fileList, function (thisFile, f) {
                console.log('Starting upload #', f);
                fileReader(thisFile.file).then(
                        function (fileContents) {
                            ApplicationFactory.createNewUploadedDocument(thisFile, thisFile.templatetype, fileContents, thisFile.description, $scope.userID, tmpMerchData.id, $applicationID, isoID).then(
                                    function successCallBack(response) {
                                        if (response.data.status === 1) {
                                            console.log('Document creation succeeded');
                                            $scope.uploadProgress = {
                                                'progress': 100,
                                                'index': f
                                            };
                                        } else {
                                            console.log('Document creation failed');
                                        }
                                    }, function errorCallback(response) {
                                console.debug('Error returned from createNewUploadedDocument ', response);
                            });
                        });
            });
        }
    };

【讨论】:

  • 虽然f 现在被困在一个闭包中,$scope.uploadProgress 可能(取决于其性质)仍然是实际进展的不可靠指标。由于ApplicationFactory.createNewUploadedDocument() 是异步的,因此无法保证其响应会以原始顺序返回。如果它是温度计式指示器,它可能会到处乱跳并在 100% 以外的地方完成。
  • 因此,使用'index': ++count 代替'index': f,其中var count = 0$scope.fileList.forEach() 循环之外声明。有了它,您不再需要关闭,因此可以恢复到 for 循环。
  • 其实,我并不依赖$scope.uploadProgress作为上传进度的实际指标,文件一完成就直接跳转到100%。不过,我明白你的意思。
  • 好吧,正如我所担心的那样,我不了解设计的很多地方。祝你好运。
猜你喜欢
  • 1970-01-01
  • 2015-05-10
  • 1970-01-01
  • 2015-11-28
  • 1970-01-01
  • 2021-09-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多