Skip to content

Instantly share code, notes, and snippets.

@sintaxi
Last active July 9, 2016 22:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sintaxi/5f7be7099a3d26f544522398f370cb3f to your computer and use it in GitHub Desktop.
Save sintaxi/5f7be7099a3d26f544522398f370cb3f to your computer and use it in GitHub Desktop.
A JavaScript question. What is the best way to handle this situation?
var expensiveThing = function(callback){
setTimeout(function(){
callback(null, { name: "Thurston Moore" })
}, 5000)
}
var fetchAndLogExpensiveThing = function(){
expensiveThing(function(err, data){
console.log(err, data)
})
}
// We need to do something expensive. So we do.
fetchAndLogExpensiveThing()
// We need to do the same thing several more times.
// How do we make the following requests use the result
// of the first expensive thing that we called and are
// waiting for?
setTimeout(fetchAndLogExpensiveThing, 1000)
setTimeout(fetchAndLogExpensiveThing, 2000)
setTimeout(fetchAndLogExpensiveThing, 3000)
setTimeout(fetchAndLogExpensiveThing, 4000)
// To be clear the last call should use the result of the
// first fetchAndLogExpensiveThing which will respond within
// a second. the expensiveThing() should only happen once.
@sintaxi
Copy link
Author

sintaxi commented Jun 10, 2016

To be clear, This is a performance optimization.

I could of course cache the result of the expensive thing and use that once it comes back however Im looking to solve the specific use case of many calls to a cold cache all at once. Ideally the seconds request just waits for the response of the first. Thats the idea anyway.

@edrex
Copy link

edrex commented Jun 10, 2016

Often you can just use the promise directly - create it once, and then resolve it as many times as needed.

var expensiveThing = function(){
  return new Promise(function(resolve, reject) {
    setTimeout(function(){
      callback(null, { name: "Thurston Moore" })
    }, 5000)
  });
}
var prom
var fetchAndLogExpensiveThing = function(){
  if (!prom) {
    prom = expensiveThing();
  }
  prom.then(function(err, data){
    console.log(err, data)
  })
}
// the rest is the same
...

@neilk
Copy link

neilk commented Jun 11, 2016

Followup from Twitter discussion: this seems to work. Although, it feels a bit like we are reimplementing what a web browser does... perhaps we'd also want to evict from cache if the resource had changed.

var memoize = require('memoizee'),
    rp = require('request-promise');

var getLocationPromise = memoize(function(location) {
  console.log("getting a new network resource:", location);
  return rp(location);
});

var printLocation = function(location) {
  console.log("need to print contents:", location);
  getLocationPromise(location).then(function(contents) {
    console.log(contents);
  });
}

var location = 'http://neilk.net/robots.txt';

setTimeout(function() { printLocation(location) }, 1000);
setTimeout(function() { printLocation(location) }, 1000);
setTimeout(function() { printLocation(location) }, 1000);
setTimeout(function() { printLocation(location) }, 1000);
setTimeout(function() { printLocation(location) }, 1000);

And if we run it:

$ node lpc.js
need to print contents: http://neilk.net/robots.txt
getting a new network resource: http://neilk.net/robots.txt
need to print contents: http://neilk.net/robots.txt
need to print contents: http://neilk.net/robots.txt
need to print contents: http://neilk.net/robots.txt
need to print contents: http://neilk.net/robots.txt
User-agent: *
Disallow:

Sitemap: http://neilk.net/sitemap.xml
User-agent: *
Disallow:

Sitemap: http://neilk.net/sitemap.xml
User-agent: *
Disallow:

Sitemap: http://neilk.net/sitemap.xml
User-agent: *
Disallow:

Sitemap: http://neilk.net/sitemap.xml
User-agent: *
Disallow:

Sitemap: http://neilk.net/sitemap.xml

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment