javascriptnode.jspromisedeferredwhen-js

deferred object returning before resolving


I am using the when library with Node js. I create a deffered object, place the resolve inside an encapsulated Mongoose findOne() function, and return the promise outside. But it seems my promise is always returned before the data is retrieved.

User.prototype.getProfile = function(criteria) {
    var deferred = when.defer();
    var options = {
        criteria: criteria,
        select: 'name id email'
    };
    this.User.load(options, function(err, data) {
        if (data) {
            this.name = data.name;
            this.email = data.email;
            this.id = data.id;
        } else {
            return false;
        }
        console.log(data);
        deferred.resolve();
    });
    console.log('returning promise');
    return deferred.promise;
};

Caller

User.getProfile(req.query).then(
        function success(data) {
            res.send('Hello ' + User.name);// Hello ''
        }
    );

Outputs 'returning promise' before the data


Solution

  • Yes, promise will be returned to the caller instead of the data and that is how we can take advantage of the asynchronous functions. This is the common sequence of actions in handling async calls,

    1. Make an async call.

    2. Return a Promise to the caller.

    3. At this point, caller doesn't have to wait for the result. It can simply define a then function, which knows what to do when the data is ready and move on to the next task.

    4. Later point of time, resolve (or reject, if failed) the promise when you get the result from the async call.

    5. Execute the then function on the Promise object, with the result from the async call.

    So, your code will have to be modified a little bit, like this

    User.prototype.getProfile = function(criteria) {
        var deferred = when.defer();
        var options = {
            criteria: criteria,
            select: 'name id email'
        };
        this.User.load(options, function(err, data) {
            if (err) {
                // Reject, if there is an error
                deferred.reject(err);
            } else {
                // Resolve it with actual data
                deferred.resolve(data);
            }
        });
        return deferred.promise;
    };
    

    Then your caller will do something like this

    userObject.getProfile()
        .then(function(profileObject) {
            console.log(profileObject);
            // Do something with the retrieved `profileObject`
        })
        .catch(function(err) {
            console.err("Failed to get Profile", err);
        });
    
    // Do something else here, as you don't have to wait for the data
    

    Here, caller just calls getProfile and attaches a function which says what to do with the returned data and moves on.


    Edit If you want the same object to be updated, then you can simply use similar code, but you need to preserve this in some other variable, because the binding of this happens at runtime.

    User.prototype.getProfile = function(criteria) {
        var deferred = when.defer();
        var options = {
            criteria: criteria,
            select: 'name id email'
        };
        var self = this;
        this.User.load(options, function(err, data) {
            if (err) {
                // Reject, if there is an error
                deferred.reject(err);
            } else {
                self.name = data.name;
                self.email = data.email;
                self.id = data.id;
            }
            deferred.resolve(data);
        });
        return deferred.promise;
    };