如何使用AngularJS顺序执行Rest Webservices调用?

M07*_*M07 4 web-services promise sequential angularjs

我想知道如何使用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
};
Run Code Online (Sandbox Code Playgroud)

欢迎所有关键人物.先感谢您.

For*_*say 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);
    });
}
Run Code Online (Sandbox Code Playgroud)

其中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)
}
Run Code Online (Sandbox Code Playgroud)

顺序

顺序上传只是稍微复杂一点.原则上我们需要做的是让每个操作在开始之前等待前一个操作完成.为此,我们首先创建一个由已解析的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
}
Run Code Online (Sandbox Code Playgroud)

用法

最后你附加到范围的原始uploadFiles操作.此时,您可以执行所有必要的操作来更新视图.请注意在此之前我没有触及任何全局变量.这样做的原因是为了确保多次调用uploadFilesSequentialuploadFilesParallel不会发生冲突并导致问题.

我没有对以下功能做出同样的保证.如果多次并行调用它,多个操作可能会导致奇怪的行为.

$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;
        });
};
Run Code Online (Sandbox Code Playgroud)

显然,如果您总是使用并行或始终使用顺序上传,那么您可以稍微简化一下.注意这样做是多么简单,我们所要做的就是处理视图逻辑和实际的实现,uploadFilesSequential或者uploadFilesParallel完全隐藏在我们身上.正是这种封装对于使大型系统编程成为必要.

未来

未来的JavaScript版本即将推出(EcmaScript 6),这将使顺序版本变得更加容易.您将能够重写它大致如下:

var uploadFilesSequential = Q.async(function* (files) {
    for(var i = 0; i < files.length; i++) {
        yield uploadFile(files[i])
    }
})
Run Code Online (Sandbox Code Playgroud)

哪个yield是等待承诺解决的特殊关键字.不幸的是,它可能还需要几年才能在真实世界的代码中使用,以便在浏览器中运行.