【问题标题】:Angular $q Service - Limiting Concurrency for Array of PromisesAngular $q 服务 - 限制承诺数组的并发性
【发布时间】:2015-11-24 23:40:07
【问题描述】:

可能有助于为这个问题提供一些背景背景信息:我正在构建一个 Angular 服务,该服务有助于将多部分表单数据(mp4 视频)块上传到云中的存储服务。

我正在尝试限制同时发生的未解决承诺(PUT 块数据请求)的数量。我正在使用$q.all(myArrayOfPromises).then()... 来侦听正在解决的所有块上传承诺,然后在发生这种情况时返回一个异步调用(POST 以完成文件)。我认为我的算法遇到了竞争条件,因为 $q.all() 在为具有大量块的文件安排所有作业之前被调用,但对于较小的文件则成功。

这是我的算法。

var uploadInChunks = function (file) {
   var chunkPromises = [];
   var chunkSize = constants.CHUNK_SIZE_IN_BYTES;
   var maxConcurrentChunks = 8;
   var startIndex = 0, chunkIndex = 0;
   var endIndex = chunkSize;
   var totalChunks = Math.ceil(file.size / chunkSize);
   var activePromises = 0;

   var queueChunks = function () {
      while (activePromises <= maxConcurrentChunks && chunkIndex < totalChunks) {
         var deferred = $q.defer();
         chunkCancelers.push(deferred); // array with broader scope I can use to cancel uploads as they're happening

         var fileSlice = file.slice(startIndex, Math.min(endIndex, file.size));

         chunkPromises.push(addChunkWithRetry(webUpload, chunkIndex, fileSlice).then(function () {
           activePromises--;
           queueChunks();
        });

        activePromises++;
        startIndex += chunkSize;
        endIndex += chunkSize;
        chunkIndex++;
     }
  }

  queueChunks();

  return $q.all(chunkPromises).then(function () {
     return filesApi.completeFile(file.fileId);
  });
};

即使$q.all 被过早地调用,在那个时候仍然挂起/甚至没有调度的文件块最终会被成功执行和解决。

我已经阅读了大量有关限制$q 并发性的文章,并且知道有一些库可以提供帮助,但我真的很想了解为什么这不能一直有效:)

【问题讨论】:

  • 小心promise.all,因为它不能保证根据竞争条件完成承诺。只有 promise.settle 保证解决它们,如果它在你的 promise 库中可用(不在 AngularJS 中)。我最近开始为此制定自己的解决方案:spex

标签: javascript angularjs asynchronous concurrency angular-promise


【解决方案1】:

您返回的承诺 ($q.all) 并不能真正表明您真正想要返回的承诺。在您上面的代码中,返回的承诺将在第一个 maxConcurrentChunks 得到解决后完成,因为当您将其传递给 $q.all() 时,chunkPromises 中有多少承诺。

另一种处理方式(并获得您想要的结果)是以下伪代码:

var uploadInChunks = function(file){

    //...vars...
    var fileCompleteDeferral = $q.defer();

    var queueChunks = function(){
        chunkPromises.push(nextChunk(chunkIndex).then(function () {
            activePromises--;

            if(allChunksDone()) { //could be activePromises == 0, or chunkIndex == totalChunks - 1
                fileCompleteDeferral.resolve();
            }
            else {
                queueChunks();
            }
        });
    }

    return fileCompleteDeferral.promise.then(completeFile());
}

此代码返回的 Promise 只会在所有 Promise 完成后才解析,而不仅仅是前 8 个。

【讨论】:

  • 感谢您的解释!现在我看到它是有道理的。我只推迟了前 16 个块(前 8 个 + promise.then( queueChunks() )
猜你喜欢
  • 2015-01-15
  • 2016-03-15
  • 2017-08-02
  • 1970-01-01
  • 2015-08-12
  • 1970-01-01
  • 2016-12-11
  • 1970-01-01
  • 2017-03-01
相关资源
最近更新 更多