条件中可能的异步调用

时间:2016-02-04 20:26:21

标签: javascript angularjs asynchronous

我正在重构一些我最初没写的遗留代码,而且我遇到了异步数据加载的问题。第一次打开特定模态时,会加载表示表单对象的一堆数据。然后,函数循环遍历表单的输入,并根据需要将其填充。它看起来像这样(非常简化):

component.inputs.forEach(function(input) {
    if (input.field == 'foo') {
        input.cols = 5;
        //etc.
    }

    if (input.field == 'bar') {
        DataService.getBars().then(function(data){
            data.forEach(function(e){
                input.options.push(e.description);
            });
        };
    }

    if (input.field == 'baz') {
        input.pattern = /regex/;
        //etc.
    }
});

return component;

问题当然是,如果我的input.field是'bar',代码将继续运行并在解析对DataService的异步调用之前命中最终返回,因此第一次打开模态时,输入.bartions输入没有填写.options。

是否有可能在继续之前让代码等待来自DataService的promise来解决,或者是否有另一种方法来处理大多数情况下函数是同步的情况,但是必须仅在异步调用中一个案例?或者我是否通过在ifs的这个大链中包含异步调用来自我射击?

2 个答案:

答案 0 :(得分:1)

一种方法是创建一个promise并将其作为属性附加到返回的对象。

function getComponent() {
    component.inputs.forEach(function(input) {
        //create initial promise
        var $promise = $q.when(input);
        if (input.field == 'foo') {
            input.cols = 5;
            //etc.
        }
        if (input.field == 'bar') {
            //chain from initial promise
            $promise = $promise.then(function () {
                 //return promise for chaining
                 return getBarPromise(input);
            });
        }
        //attach promise to input object
        input.$promise = $promise;
    });

    var promises = [];
    angular.forEach(inputs, function(input) {
        promises.push(input.$promise);
    });
    //create composite promise
    var $promise = $q.all(promises);

    //final chain 
    $promise = $promise.then( function() {
         //return component for chaining
         return component;
    });
    //attach promise to component  
    component.$promise = $promise;

    return component;
};

返回的component对象最终将填入服务调用的结果。需要等待所有服务调用完成的函数可以从附加的$promise属性

$scope.component = getComponent();

$scope.component.$promise.then( function (resolvedComponent) {
    //open modal 
}).catch( function(errorResponse) {
    //log error response
});

因为调用promise的then方法会返回一个新的派生promise,所以很容易创建一个promise链。可以创建任何长度的链,并且由于可以使用另一个承诺(将进一步推迟其解析)来解决承诺,因此可以在链中的任何点暂停/推迟承诺的解析。这使得实现强大的API成为可能。 1

答案 1 :(得分:0)

如果您希望继续使用现有代码结构并使其工作,则可能需要使用promises。您还可以使用javascript' s map function。注意:您需要将$q注入您想要调用此函数的任何位置。

function getComponent() {
    var deferred = $q.defer(),
        deferred2 = $q.defer(),
        promises = component.inputs.map(function(input)) {
            if (input.field == 'foo') {
                input.cols = 5;
                deferred2.resolve();
            }
            else if (input.field == 'bar') {
                DataService.getBars().then(function(data) {
                    data.forEach(function(e){
                        input.options.push(e.description);
                    });
                    deferred2.resolve();
                }).catch(function(err)) {
                    deferred2.reject(err);
                });
            }
            else if (input.field == 'baz') {
                input.pattern = /regex/;
                deferred2.resolve();
            }

            return deferred2.promise;
        });

    $q.all(promises)
        .then(function() {
            deferred.resolve(component);
        }).catch(function(err) {
            deferred.reject(err);
        });

    return deferred.promise;
}

input中的component.inputs进行适当分析后,$q.all块会触发,您可以返回新的component对象。

最后,要设置component对象,只需执行以下操作:

getComponent().then(function(result)) {
        //Set component object here with result
        $scope.component = result;
    }).catch(function(err) {
        // Handle error here
    });