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

waits for async data in AngularJS or Javascript (Browser)

我正在 AngularJS 中为 Odoo 服务器编写一个服务包装器,它具有服务器支持的所有方法和 return 一个 延迟承诺 当服务被调用时。例如:

$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:给你一个关于你查询的模型的所有内容的列表 fields_get:给你模型拥有的字段列表 还有更多。

通常在一个工作的应用程序中,我们需要调用2个或更多的API方法来获得我们想要的最终数据。但是,因为在 Java 中,一切正常 asynchronously.The 我想象的代码会嵌套和复杂。

因此,当我进行每个 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);
                            });
                        }
                    }
                )
            }           
        }
    );
}

Vs 如果我可以同步获取那些 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});
        }
    }
}

请指教。谢谢。

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

我们可以看到它相对于 UI 的优势,我们可以在回调中放置耗时较长的任务,并且可以避免 UI 变灰。 所以我建议采用aync方法。

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

Angular 为您提供了 $q.when(method()).then(successCallback(response));,只有当 method() 执行完成并且method() 响应可用于进行另一个 promise 调用。这种方法将帮助您降低代码的复杂性,因为您试图制作传统上不正确的回调链。

$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
                )
}

异步优于同步。

然而,回调会变得非常混乱,所以我们有承诺。然而,承诺也可能变得混乱,尽管没有那么糟糕。 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 的一部分(let/const、解构、箭头函数、模板字符串、模块、unicode 改进、扩展运算符/剩余参数、改进的对象文字,可能还有 class 文字),这些东西你最常使用的/不太难转译。也许还可以使用 async-await:它不是 ES6 或 ES2016 的一部分,但它现在处于第 3 阶段,因此非常稳定,并且它确实使异步代码更容易阅读。需要注意的是,您必须转译新的 ES6/ES2016/etc。使用 Babel or TypeScript 的功能,并为承诺使用 polyfill(async-await 在内部使用)。

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

这里 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