我想知道如何使用AngularJS调用顺序Web服务? 为了简化,当我们得到第一个web服务的结果时,我们称之为第二个。 我已经找到了解决方案,但我并不喜欢它。我认为必须有更好的解决方案,这就是我发布问题的原因。 这是我的代码(并且有效)。
var uploadFile = function(file) {
szUrl = globalServ.datas.szUrl+"transactions/"+globalServ.transactionId+"/file";
var postData = {};
postData['name'] = "MyFile"+file;
postData['file'] = $scope.wsFiles[file];
$http({
method: 'POST',
url:szUrl,
headers: {'Content-Type': false, 'Authorization': globalServ.make_base_auth()},
transformRequest: formDataObject,
data:postData,
}).success(function(response) {
$scope.qFiles[file].resolve(file);//This will allow to call "then" method
}).error(function (data, status, headers, config) {
$scope.errorMsg = globalServ.getErrorMsg(status, data);
$scope.filesAdded = false;
});
}
$scope.uploadFiles = function() {
delete $scope.errorStatus;
$scope.qFiles = [];
$scope.uploadOver = false;
for(file in $scope.wsFiles) {
var q1 = $q.defer();
q1.promise.then(function(curFile) {
if(curFile < $scope.wsFiles.length - 1) {
uploadFile(curFile+1);
} else {
$scope.uploadOver = true;//We uploaded all contracts so now can stop to show loading button
}
});
$scope.qFiles.push(q1);
}
uploadFile(0);//We start to upload contracts
};
欢迎所有人批评。提前谢谢。
答案 0 :(得分:8)
首先接受如果你要使用promises,你想在任何地方使用它们。他们已经提供了许多你想要手工创建的结构。此外,除非绝对必要,否则请尽量避免向$scope
(或任何其他全局)添加内容。构建所有实用程序函数,使它们独立于范围和任何视图更新等。
接下来要做的是让uploadFile
返回一个承诺。我们还希望它不使用$ scope,而是将所需的所有信息作为参数。因此,我建议如下:
function uploadFile(file) {
szUrl = globalServ.datas.szUrl+"transactions/"+globalServ.transactionId+"/file";
var postData = {};
postData['name'] = "MyFile"+file.name;
postData['file'] = file.data;
return $http({ // <--------------------------- return the promise here
method: 'POST',
url:szUrl,
headers: {'Content-Type': false, 'Authorization': globalServ.make_base_auth()},
transformRequest: formDataObject,
data:postData,
}).then(function onSuccess(response) { //<--- `.then` transforms the promise here
return file
}, function onError(response) {
throw globalServ.getErrorMsg(response.status, response.data);
});
}
其中file是{name: 'file name', data: 'contents of file post'}
对于上传文件,我们必须决定是要将它们并行还是按顺序上传。并行通常会更快,但会使用更多资源(内存,带宽等)。如果你需要同时做很多其他事情,你可能想要连续完成它们。您可能还想选择上传的第三个选项(此处未涵盖),并且最多同时发送5个。
无论哪种方式,我们将创建一个函数,它接受一组file
个对象并上传它们,然后返回一个在操作完成时解析的promise,如果失败则拒绝。
要并行执行此操作,我们只需上传每个文件,然后使用$q.all
等待所有上传完成。
function uploadFilesParallel(files) {
var uploads = []
for(var i = 0; i < files.length; i++) {
uploads.push(uploadFile(files[i]))
}
return $q.all(uploads)
}
顺序上传只是稍微复杂一点。原则上我们需要做的是让每个操作在开始之前等待前一个操作完成。为此,我们首先创建一个由已解析的promise表示的虚拟初始操作。然后,我们通过循环跟踪上一个操作,以便我们可以在上传之前始终等待上一个操作。
function uploadFilesSequential(files) {
var previous = $q.when(null) //initial start promise that's already resolved
for(var i = 0; i < files.length; i++) {
(function (i) {
previous = previous.then(function () { //wait for previous operation
return uploadFile(files[i])
})
}(i)) //create a fresh scope for i as the `then` handler is asynchronous
}
return previous
}
最后你附加到范围的原始uploadFiles操作。此时,您可以执行所有必要的操作来更新视图。请注意在此之前我没有触及任何全局变量。这样做的原因是为了确保对uploadFilesSequential
或uploadFilesParallel
的多次调用不会发生冲突并导致问题。
我对以下功能没有做出同样的保证。如果多次并行调用它,多次操作可能会导致奇怪的行为。
$scope.uploadFiles = function (parallel) {
delete $scope.errorStatus;
$scope.uploadOver = false;
var files = [];
for(file in $scope.wsFiles) {
files.push({
name: file,
data: $scope.wsFiles[file]
})
}
var uploadOperation
if (parallel) {
uploadOperation = uploadFilesParallel(files)
} else {
uploadOperation = uploadFilesSequential(files)
}
uploadOperation
.then(function(file) {
$scope.uploadOver = true;
}, function (err) {
$scope.errorMsg = err;
$scope.filesAdded = false;
});
};
显然,如果您总是使用并行或始终使用顺序上传,那么您可以稍微简化一下。注意这样做有多简单,我们所要做的就是处理视图逻辑,uploadFilesSequential
或uploadFilesParallel
的实际实现完全隐藏在我们面前。正是这种封装对于使大型系统编程变得至关重要。
即将推出的JavaScript版本(EcmaScript 6)将使顺序版本变得更加容易。您将能够重写它大致如下:
var uploadFilesSequential = Q.async(function* (files) {
for(var i = 0; i < files.length; i++) {
yield uploadFile(files[i])
}
})
其中yield
是一个特殊的关键字,等待承诺得到解决。不幸的是,它可能还需要几年才能在真实世界的代码中使用,以便在浏览器中运行。
答案 1 :(得分:0)
Javascript调用始终是异步的。但据我所知,使javascript调用同步的唯一方法是在第一次请求的succes / error处理程序中调用新请求。
答案 2 :(得分:0)
我认为你想建立一个.then()
链,如下所示:
var uploadFile = function(index) {
return $http({
method: 'POST',
url: globalServ.datas.szUrl + "transactions/" + globalServ.transactionId + "/file",
headers: {
'Content-Type': false,
'Authorization': globalServ.make_base_auth()
},
transformRequest: formDataObject,
data: {
name: "MyFile" + index,
file: $scope.wsFiles[index]
}
}).error(function (data, status, headers, config) {
$scope.errorMsg = globalServ.getErrorMsg(status, data);
$scope.filesAdded = false;
});
}
$scope.uploadFiles = function() {
delete $scope.errorStatus;
$scope.uploadOver = false;
var p = $q.defer().resolve().promise;//Ready-resolved starter promise.
function makeClosure(i) {
return function() {
return uploadFile(i);
}
}
//Build a .then() chain
for(var i=0; i<$scope.wsFiles.length; i++) {
p = p.then(makeClosure(i));
}
//Add a terminal .then() to the chain.
p.then(function() {
$scope.uploadOver = true;//We uploaded all contracts so now can stop to show loading button
});
};
我必须做出几个假设,所以这可能不是100%正确,但应该让你知道如何继续。