1
votes

I have an array of objects coming back from $http.get that I need to do three different autonomous post processes on. I believe I can use $q.all to execute the post processes in parallel.

I see a ton of examples that use $timeout/setTimeout that follow this pattern:

function doSomethingAsync() {
    var deferred = $q.defer();

    setTimeout(function() {
        deferred.resolve('hello world');
    }, 500);

    return deferred.promise;
}

doSomethingAsync().then(function(val) {
    console.log('Promise Resolved!', val);
});

I'm new to javascript, but this doesn't get at the heart of my problem. The $timeout and setTimeout function are already asynchronous and therefore the doSomethingAsync just falls through returning the promise...no actual work was done inline.

In the following example, I don't understand how the function returns the promise immediately upon execution. Will the "return deferred.promise" execute before the loop is complete? Wouldn't the function be forced to execute the loop first and then the return statement?

function doSomethingRealWorkAsync() {
    var deferred = $q.defer();

    //loop through the items in an array and perform some heavy lifting that will take time

    deferred.resolve('Done!');
    return deferred.promise;
}

doSomethingRealWorkAsync().then(function(val) {
    console.log('Promise Resolved!', val);
});

Every example I've looked at uses an async process (either $timeout or setTimeout) to simulate work being done. What about real work? What if I have multiple long running processes that I want to run in parallel?

Using the above pattern would yield the following code, but I can't imagine it working correctly. Will this work? If not, why? If so, how?

$scope.items = [{}];
$scope.initialized = false;

function doOneThingToItemsAsync() {
    var deferred = $q.defer();

    for (var i in items) {
        items[i].propertyA = 'this';
    }
    deferred.resolve('Done with One Thing!');
    return deferred.promise;
}

function doAnotherThingToItemsAsync() {
    var deferred = $q.defer();

    for (var i in items) {
        items[i].propertyB = 'that';
    }

    deferred.resolve('Done with Another Thing!');
    return deferred.promise;
}

function doYetAnotherThingToItemsAsync() {
    var deferred = $q.defer();

    for (var i in items) {
        items[i].propertyC = 'I smell a cat';
    }

    deferred.resolve('Done with Yet Another Thing!');
    return deferred.promise;
}

function getItems () {
    $http.get("/api/widgets", { timeout: 0 })
        .success(function (data) {
            items = data.items;
            $q.all([doOneThingToItemsAsync(), 
                    doAnotherThingToItemsAsync(),
                    doYetAnotherThingToItemsAsync()])
                .then(function(result) {
                    for (var i in result) {
                        console.log(result[i]);
                    }
                    initialized = true;
                });
        })
        .error(function (data) {
            if (data.errorMessage) {
                console.log("There was a problem retrieving your data: \n" + data.errorMessage + "\nPlease try again.");
            } else {
                console.log("There was a problem retrieving your data.  Please try again.");
            }
        });
}

Does the browser's interpreter evaluate the type being returned on the function (in this case a promise) and allow you to call methods on that type (e.g. then, success, error) passing in callback functions that will be executed upon executing deferred.resolve, deferred.reject, etc.?

2
Maybe you need WebWorkers if you need to process a lot of data in parallel?dfsq
@dfsq, thanks for the comment. Would WebWorkers have access to angular scope and service variables? Being seperate js files, I thought they had limited access.Greg Grater

2 Answers

1
votes

No, as you have realized you will need to make sure that the promise is returned before any actual work is done. So doing work inline before returning the promise will not work.

Basically the idea is having an inline function that only creates a deferred object and returns the promise from it, then having an async operation that does the actual work and that has a access to the deffered object through a closure and can set it to resolved once the work is done.

So you could take your examples and do something like this:

function doSomethingAsync() {
var deferred = $q.defer();

setTimeout(function() {
   // do the actual work
   deffered.resolve();
}, 500);

return deferred.promise;
}

doSomethingAsync().then(function() {
console.log('Promise Resolved! and work is done');
}); 
1
votes

Will the "return deferred.promise" execute before the loop is complete? Wouldn't the function be forced to execute the loop first and then the return statement?

Yes. Loops are synchronous.

Every example I've looked at uses an async process (either $timeout or setTimeout) to simulate work being done. What about real work? What if I have multiple long running processes that I want to run in parallel?

JavaScript doesn't run in parallel. Really heavy work typically is involving IO, and that is async anyway. If you're doing heavy processing with JS itself (which might not be the best choice), you should a) split up the work in smaller chunks which run sequentially with timeouts (async), or b) do the processing in a separate environment, i.e. a WebWorker, with async communication.

Using the above pattern would yield the following code, but I can't imagine it working correctly. Will this work?

It will "work", but it won't run asynchronous, let alone in parallel.

Does the browser's interpreter evaluate the type being returned on the function (in this case a promise) and allow you to call methods on that type (e.g. then, success, error) passing in callback functions that will be executed upon executing deferred.resolve, deferred.reject, etc.?

No interpreter magic is going on here. Promises are ordinary objects with ordinary methods that can be created by user libraries, and the usual semantics apply to them.