Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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);
like image 987
pb_ Avatar asked Jul 15 '15 04:07

pb_


People also ask

Can Promises resolve multiple times?

No. It is not safe to resolve/reject promise multiple times. It is basically a bug, that is hard to catch, becasue it can be not always reproducible.

Can a promise be Cancelled?

Promises have settled (hah) and it appears like it will never be possible to cancel a (pending) promise. Instead, there is a cross-platform (Node, Browsers etc) cancellation primitive as part of WHATWG (a standards body that also builds HTML) called AbortController .


1 Answers

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 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
});
like image 50
jfriend00 Avatar answered Sep 23 '22 19:09

jfriend00