Immediately return a resolved promise using AngularJS
Asked Answered
N

5

29

I'm trying to get my head around promises in JavaScript (in particular AngularJS).

I have a function in a service, let's call it fooService, that checks if we've loaded some data. If it has, I just want it to return, and if we haven't, we need to load the data and return a promise:

this.update = function(data_loaded) {
    if (data_loaded) return;  // We've loaded the data, no need to update

    var promise = Restangular.all('someBase').customGet('foo/bar').then(function(data) {
        // Do something with the data here
    }

    return promise;
}

I have another function that then calls the update function of fooService like so:

fooService.update(data_loaded).then(function() {
    // Do something here when update is finished
})

My issue here is that if we don't need to load the data in the update function, a promise isn't returned, so the .then() is not called in my other function. What should the approach be here - basically I want to return a resolved promise immediately from the update() function if we do not need to get data from the Restangular call?

Newel answered 17/7, 2014 at 3:29 Comment(0)
B
27

The current accepted answer is overly complicated, and abuses the deferred anti pattern. Here is a simpler approach:

this.update = function(data_loaded) {
    if (data_loaded) return $q.when(data);  // We've loaded the data, no need to update

    return Restangular.all('someBase').customGet('foo/bar')
                             .then(function(data) {
        // Do something with the data here 
    });
};

Or, even further:

this._updatep = null;
this.update = function(data_loaded) { // cached
    this._updatep = this._updatep || Restangular.all('someBase') // process in
                                                .customGet('foo/bar'); //.then(..
    return this._updatep;
};
Boer answered 17/7, 2014 at 5:7 Comment(1)
Thanks for a tip. I always missed the Q(initialVal) syntax of Kris Kowal's Q and should have known $q.when(data) can do the same trick.Contributory
P
51

As your promise use the same syntax as the JavaScript native one, you could use and return an already resolved JavaScript promise : Promise.resolve()

return(Promise.resolve("MyReturnValue"));
Pryer answered 9/7, 2015 at 8:56 Comment(4)
This is the answer I wanted; I have a value; it's ready, I just want to wrap it in a Promise to satisfy APIs which expect Promises... So while the OP asked, "Immediately return a resolved promise", what I wanted was to "return a Promise , which I resolved immediately (with a literal)"Midrash
That's what I wanted too. I have an object and just need to return a promise since that's what the API expects.Bubonocele
Careful for IE support on this answer. $q provides cross browser supportMatador
@JosefE., good point regarding IE support. The ideal answer would address both the modern Javascript case (in a modern browser or using a transpiler) and the legacy case. Let us pray to the Microsoft gods that IE soon becomes obsolete.Kellsie
B
27

The current accepted answer is overly complicated, and abuses the deferred anti pattern. Here is a simpler approach:

this.update = function(data_loaded) {
    if (data_loaded) return $q.when(data);  // We've loaded the data, no need to update

    return Restangular.all('someBase').customGet('foo/bar')
                             .then(function(data) {
        // Do something with the data here 
    });
};

Or, even further:

this._updatep = null;
this.update = function(data_loaded) { // cached
    this._updatep = this._updatep || Restangular.all('someBase') // process in
                                                .customGet('foo/bar'); //.then(..
    return this._updatep;
};
Boer answered 17/7, 2014 at 5:7 Comment(1)
Thanks for a tip. I always missed the Q(initialVal) syntax of Kris Kowal's Q and should have known $q.when(data) can do the same trick.Contributory
A
6

AngularJS's $q service will help you here. It is much like Kris Kowal's Q promise library.

When you have an async method that may return a promise or value use the $q.when method. It will take what ever is passed to it, be it a promise or a value and create a promise that will be resolved/rejected based on the promise passed, or resolved if a value is passed.

$q.when( fooService.update(data_loaded) ).then(function(data){
   //data will either be the data returned or the data
   //passed through from the promise
})

and then in your update function return the data instead of just returning

if (data_loaded) return data_loaded;
Admonition answered 17/7, 2014 at 3:39 Comment(0)
T
0

Similar to Elo's answer, you can return an already resolved promise using the async/await syntax:

this.update = async (data_loaded) => {

    if (data_loaded) 
        return await null;  // Instead of null, you could also return something else
                            // like a string "Resolved" or an object { status: 200 }
    else 
        return await OtherPromise();
}
Trice answered 23/7, 2018 at 11:27 Comment(0)
C
-2

You could use the $q.defer() like this:

this.update = function (data_loaded) {
    var deferred = $q.defer();

    if (data_loaded) {
        deferred.resolve(null); // put something that your callback will know the data is loaded or just put your loaded data here.
    } else {
        Restangular.all('someBase').customGet('foo/bar').then(function(data) {
            // Do something here when update is finished
            deferred.resolve(data);
        }
    }

    return deferred.promise;
};

Hope this helps.

Contributory answered 17/7, 2014 at 3:37 Comment(5)
I have no idea how this got upvoted twice, this is the deferred anti pattern and can be greatly simplified.Boer
It's not only the deferred antipattern, it also fails to propagate errors.Medeah
@Benjamin IMHO, this is not exactly the same as the deferred anti pattern. The deffered anti pattern you mention is when the deferred can be removed completely without a lose of functionality. But yes, your answer is much simpler and also handle error well.Contributory
@samtuner please accept the Benjamin's answer instead of this.Contributory
@Medeah imo the biggest problem with this solution is the fact it will make multiple requests instead of one if this method is called several times before the data is returned since the data is cached instead of the promise and there is no a "already sent" flag. Error handling is a biggie too though so you're right there.Boer

© 2022 - 2024 — McMap. All rights reserved.