Prashant Baid Prashant Baid - 5 months ago 49
Node.js Question

How to reuse promises?

I am trying to reuse the the data returned from promise here. But, the problem is, after the first call to

checkPromise
function, it immediately calls the second function, and the promise for the first function is not fulfilled, so it never returns any data, and hence it never enters the if clause. How do I reuse a promise?

var Promise = require('bluebird');
var request = Promise.promisify(require("request"));


var url = 'http://www.google.com';
var obj = new Object;

function apiCall(url) {
return new Promise(function (resolve, reject) {

request(url).spread(function(response, body) {
return resolve(body);
}).catch(function(err) {
console.error(err);
return reject(err);
});

});
}

function checkPromise(url) {
if(obj.hasOwnProperty(url)) {
var rp = obj[url];
//do something
}
else {
apiCall(url).then(function(result) {
obj[url] = result;
//do something
});
}
}

checkPromise(url);
checkPromise(url);

Answer

You likely have a timing issue. Your apiCall() function is asynchronous. That means it finishes sometime later. As such, each time you call checkPromise(), all you're doing is starting a request and it finishes sometime later. So, you call it the first time and it starts a request (that has not finished yet). Then, your next call to checkPromise() gets called and it does it's if check before the first call has completed. Thus, it finds nothing in the cache yet.

Your code is running two requests in parallel, not one after the other.

If you actually want to wait until the first request is done before executing the second one, then you will have to actually structure your code to do that. You would need to make checkPromise() return a promise itself so code using it could known when it was actually done in order to execute something after it was done.

FYI, I don't see anything in your code that is actually related to reusing promises (which is something you cannot do because they are one-shot objects).

Here's one possible implementation:

var Promise = require('bluebird');
var request = Promise.promisify(require("request"));

var url = 'http://www.google.com';
var obj = {};

function apiCall(url) {
    return request(url).spread(function(response, body) {
        return body;
    });
}

function checkPromise(url) {
    if(obj.hasOwnProperty(url)) {   
        var rp = obj[url];
        //do something
        return Promise.resolve(rp);
    }
    else {
        return apiCall(url).then(function(result) {            
            obj[url] = result; 
            //do something
            return result;
        });
    }
}

checkPromise(url).then(function() {
    checkPromise(url);
});

Significant changes:

  1. Return the promise returned by request() rather than create yet another one.
  2. Change checkPromise() so it always returns a promise whether the value is found in the cache or not so calling code can always work consistently.
  3. Sequence the two checkPromise() calls so the first can finish before the second is executed.

A very different approach would be to actually wait on the cache if a result you are interested in is already being loaded. That could be done like this:

var Promise = require('bluebird');
var request = Promise.promisify(require("request"));

var url = 'http://www.google.com';
var obj = {};

function apiCall(url) {
    return request(url).spread(function(response, body) {
        return resolve(body);
    });
}

function checkPromise(url) {
    if(obj.hasOwnProperty(url)) {   
        // If it's a promise object in the cache, then loading 
        // If it's a value, then the value is already available
        // Either way, we wrap it in a promise and return that
        return Promise.resolve(obj[url]);
    } else {
        var p = apiCall(url).then(function(result) {
            obj[url] = result; 
            //do something
            return result;
        });
        obj[url] = p;
        return p;
    }
}

checkPromise(url).then(function(result) {
    // use result
});

checkPromise(url).then(function(result) {
    // use result
});
Comments