在AngularJS或Javascript(浏览器)中等待异步数据

时间:2016-06-05 10:29:01

标签: javascript angularjs async-await openerp

我在AngularJS中为Odoo Server编写Service Wrapper,它具有服务器支持的所有方法,并在调用服务时返回延迟承诺。 E.g:

$scope.runRPC = function(){
    odooSvc.fields_get('res.users').then(
        function(result){
            console.log(result); //return a list of users 
        }
    );
}

但是,我需要它是同步的,这就是原因所在。

在Odoo中,它有自己的JSON rpc API,它有几种相互依赖的方法。

例如,

search_read:给你一个关于模型u查询的所有内容的列表 fields_get:给你模型所具有的字段列表 还有更多。

通常在工作的应用程序中,我们需要调用2个或更多API方法来获取我们想要的最终数据。但是,因为在Java中,一切都是异步工作的。代码I图像将是简洁和复杂的。

因此,当我进行每个API调用时,彼此依赖。它看起来像这样:

$scope.setLoginToContactEmail = function(){
    odooSvc.search_read('res.users').then(
        function(users){
            for(var i=0; i < user.length; i++){
                user = users[0];
                login = user.login
                partner_id = user.partner_id

                odooSvc.read_model('res.partner', partner_id).then(
                    function(partner){
                        if(login === partner.email){
                            odooSvc.write_model('res.partner', partner_id, {email: login}).then(function(msg){
                                console.log(msg);
                            });
                        }
                    }
                )
            }           
        }
    );
}

如果我可以同步运行这些API,或等待数据到达,然后再进行另一次调用。看起来会更简单:

$scope.setLoginToContactEmail = function(){
    var users = odooSvc.search_read('res.users');
    for(var i=0; i < user.length; i++){
        user = users[0];
        login = user.login
        partner_id = user.partner_id

        partner = odooSvc.read_model('res.partner', partner_id);
        if (login === partner.email){
            odooSvc.write_model('res.partner', partner_id, {email: login});
        }
    }
}

请指教。感谢。

3 个答案:

答案 0 :(得分:1)

异步优于同步。

然而,回调可能变得非常混乱,所以我们有承诺。然而,承诺也会变得混乱,尽管不是那么糟糕。 Async-await是获取同步异步代码的最佳方式,但您必须进行转换。这取决于您想要使用多少工具。

以下是我编写ES5代码的方法(不在.then(之后开始新行,这会减少一些缩进,我也在for循环中做了一些更改,因为我不确定你的意思):

$scope.setLoginToContactEmail = function () {
    odooSvc.search_read('res.users').then(function (users) {
        for (var i = 0; i < users.length; i++) {
            var user = users[i]
            var login = user.login
            var partner_id = user.partner_id

            odooSvc.read_model('res.partner', partner_id).then(function (partner) {
                if (login === partner.email) {
                    odooSvc.write_model('res.partner', partner_id, { email: login }).then(function (msg) {
                        console.log(msg)
                    })
                }
            })
        }
    })
}

使用ES6和异步函数的提议可以成为:

$scope.setLoginToContactEmail = async function () {
    const users = await odooSvc.search_read('res.users')
    for (let user of users) {
        const { login, partner_id } = user
        const partner = await odooSvc.read_model('res.partner', partner_id)
        if (login === partner.email) {
            const msg = await odooSvc.write_model('res.partner', partner_id, { email: login })
            console.log(msg)
        }
    }
}

这取决于你想要做多少转化。就个人而言,我会采用ES6的一部分(让/ const,解构,箭头函数,模板字符串,模块,unicode改进,扩展操作符/ rest参数,改进的对象文字,以及可能的类文字),你最常使用的东西/并不难以透露。也许还使用async-await:它不是ES6或ES2016的一部分,但它现在处于第3阶段,因此非常稳定,并且它确实使异步代码更容易阅读。需要注意的是,您必须转换新的ES6 / ES2016 /等。使用BabelTypeScript的功能,并使用polyfill进行承诺(async-await在内部使用)。

TL; DR:如果你发现自己陷入异步地狱,那么async-await提案可能是最好的解决方案。

答案 1 :(得分:1)

这是a plunker照顾Babel翻译:

  <body ng-app="app" ng-controller="AsyncController">
    <p>{{ message }}</p>
  </body>
angular.module('app', []).controller('AsyncController', ['$timeout', '$scope', async function ($timeout, $scope) {
  $scope.message = 'no timeout';  

  $scope.message = await $timeout(() => 'timeout', 2000);
  $scope.$apply();
}]);

async...await与TypeScript和ES.next中的一样简单。这里应该注意两件事。

第一个是异步控制器内的this上下文 - 它可能与预期的不同。当使用类并且必要时绑定异步方法时,这可能不是问题。当构造函数是异步的并且从一开始就无法到达this时,这是非OOP代码的问题。

另一个是async...await由本机Promises提供支持。这意味着应该调用$scope.$apply()来触发摘要周期,尽管已经使用了对摘要友好的$timeout

答案 2 :(得分:0)

异步方法是我们必须使用的JavaScript的强大功能。原因是,如果任务需要很长时间才能执行,那么我们就不必等待,同时可以执行其他任务。

我们可以看到它在UI上的优势,我们可以在回调中放置长时间的任务,并且可以避免UI变灰。 所以我建议采用异步方法。

但是你决定执行方法的方式并不是Angular方式的更好方法。

Angular已经为你提供了$ q.when(method())。then(successCallback(response));通过它控制只有在完成method()并且可以使用method()响应时才会在successCallback中出现另一个承诺电话。这种方法可以帮助您降低代码的复杂性,因为您试图制作传统上不正确的回调链。

$scope.setLoginToContactEmail = function(){
    $q.when(searchRead()).then(function(res) {
        modelRead(res);
    });
}


function searchRead() {
    odooSvc.search_read('res.users').then(
            // @TODO       
        }
    );

}

function modelRead(res) {
    odooSvc.read_model('res.partner').then(
                    // @TODO
                )
}