Angular $q 服务 - 限制承诺数组的并发
Angular $q Service - Limiting Concurrency for Array of Promises
可能有助于为这个问题提供一些背景信息:我正在构建一个 angular 服务,该服务有助于将多部分表单数据(mp4 视频)块上传到云中的存储服务。
我正在尝试限制同时发生的未解决承诺(PUT
块数据请求)的数量。我正在使用 $q.all(myArrayOfPromises).then()...
来监听所有正在解决的块上传承诺,然后在发生这种情况时 return 异步调用(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
的并发性的文章,并且知道有一些库可以提供帮助,但我真的很想了解为什么这不起作用一直:)
您 return 的承诺 ($q.all
) 并不代表您真正想要 return 的承诺。在您上面的代码中,returned 承诺将在第一个 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());
}
此代码 return 的承诺将仅在所有承诺完成后才解决,而不仅仅是前 8 个。
可能有助于为这个问题提供一些背景信息:我正在构建一个 angular 服务,该服务有助于将多部分表单数据(mp4 视频)块上传到云中的存储服务。
我正在尝试限制同时发生的未解决承诺(PUT
块数据请求)的数量。我正在使用 $q.all(myArrayOfPromises).then()...
来监听所有正在解决的块上传承诺,然后在发生这种情况时 return 异步调用(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
的并发性的文章,并且知道有一些库可以提供帮助,但我真的很想了解为什么这不起作用一直:)
您 return 的承诺 ($q.all
) 并不代表您真正想要 return 的承诺。在您上面的代码中,returned 承诺将在第一个 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());
}
此代码 return 的承诺将仅在所有承诺完成后才解决,而不仅仅是前 8 个。