Search code examples
node.jspromisedelaysleep

Multiple delays in Javascript/Nodejs Promise


I'm working on a proxy that caches files and I'm trying to add some logic that prevents multiple clients from downloading the same files before the proxy has a chance to cache them.

Basically, the logic I'm trying to implement is the following:

Client 1 requests a file. The proxy checks if the file is cached. If it's not, it requests it from the server, caches it, then sends it to the client.

Client 2 requests the same file after client 1 requested it, but before the proxy has a chance to cache it. So the proxy will tell client 2 to wait a few seconds because there is already a download in progress.

A better approach would probably be to give client 2 a "try again later" message, but let's just say that's currently not an option.

I'm using Nodejs with the anyproxy library. According to the documentation, delayed responses are possible by using promises.

However, I don't really see a way to achieve what I want using Promises. From what I can tell, I could do something like this:

module.exports = {
  *beforeSendRequest(requestDetail) {
    if(thereIsADownloadInProgressFor(requestDetail.url)) {
        return new Promise((resolve, reject) => {
            setTimeout(() => { // delay
              resolve({ response: responseDetail.response });
            }, 10000);
        });
    }
  }
};

But that would mean simply waiting for a maximum amount of time and hoping the download finishes by then. And I don't want that.

I would prefer to be able to do something like this (but with Promises, somehow):

module.exports = {
  *beforeSendRequest(requestDetail) {
    if(thereIsADownloadInProgressFor(requestDetail.url)) {
        var i = 0;
        for(i = 0 ; i < 10 ; i++) {
            JustSleep(1000);

            if(!thereIsADownloadInProgressFor(requestDetail.url))
                return { response: responseDetail.response };
        }
    }
  }
};

Is there any way I can achieve this with Promises in Nodejs?

Thanks!


Solution

  • You can use a Map to cache your file downloads.

    The mapping in Map would be url -> Promise { file }

    // Map { url => Promise { file } }
    const cache = new Map()
    
    const thereIsADownloadInProgressFor = url => cache.has(url)
    
    const getCachedFilePromise = url => cache.get(url)
    
    const downloadFile = async url => {/* download file code here */}
    
    const setAndReturnCachedFilePromise = url => {
      const filePromise = downloadFile(url)
      cache.set(url, filePromise)
      return filePromise
    }
    
    module.exports = {
      beforeSendRequest(requestDetail) {
        if(thereIsADownloadInProgressFor(requestDetail.url)) {
            return getCachedFilePromise(requestDetail.url).then(file => ({ response: file }))
        } else {
            return setAndReturnCachedFilePromise(requestDetail.url).then(file => ({ response: file }))
        }
      }
    };