Caching JavaScript promise results

前端 未结 6 858
独厮守ぢ
独厮守ぢ 2021-01-03 01:55

I would make one call to the server to get a list of items. How do I make sure that only one call is made and the collections is processed only once to create a key value ma

相关标签:
6条回答
  • 2021-01-03 02:13

    I think what you're really looking for is

    var cache = null; // cache for the promise
    function getItems() {
        return cache || (cache = getAllItemsFromServer().then(function(data){
            var itemMap = {};
            data.forEach(function(value){
                itemMap[value.key] = value;
            });
            return itemMap;
        }));
    }
    
    var itemKeys = ['a', 'b', 'c'];
    itemKeys.forEach(function(key){
        getItems().then(function(data){
            return data[key];
        }).then(function(value) {
            console.log('item for key=' + value);
        }); // notice that you get the value only asynchronously!
    });
    
    0 讨论(0)
  • 2021-01-03 02:15

    Try npm dedup-async, which caches duplicated promise calls if there's a pending one, so concurrent promise calls trigger only one actual task.

    const dedupa = require('dedup-async')
    
    let evil
    let n = 0
    
    function task() {
        return new Promise((resolve, reject) => {
            if (evil)
                throw 'Duplicated concurrent call!'
            evil = true
    
            setTimeout(() => {
                console.log('Working...')
                evil = false
                resolve(n++)
            }, 100)
        })
    }
    
    function test() {
        dedupa(task)
            .then (d => console.log('Finish:', d))
            .catch(e => console.log('Error:', e))
    }
    
    test()                //Prints 'Working...', resolves 0
    test()                //No print,            resolves 0
    setTimeout(test, 200) //Prints 'Working...', resolves 1
    
    0 讨论(0)
  • 2021-01-03 02:22

    You could also solve this by using a decorators, For an instance by using the debounce decorator from utils-decorator lib (npm install utils-decorators):

    import {memoizeAsync} from 'utils-decorators';
    
    class MyAppComponent {
    
      @memoizeAsync(30000)
      getData(input) {
       ...
      }
    }
    
    0 讨论(0)
  • 2021-01-03 02:24

    A promise is stateful, and as soon as it's fulfilled, its value cannot be changed. You can use .then multiple times to get its contents, and you'll get the same result every time.

    The getAllItemsFromServer function returns a promise, the then block manipulates the responses and returns the itemMap, which is wrapped in a response (promise chaining). The promise is then cached and can be used to get the itemMap repeatedly.

    var itemsPromise = getItems(); // make the request once and get a promise
    
    function getItems(){
        return getAllItemsFromServer().then(function(data){
           return data.reduce(function(itemMap, value){
              itemMap[value.key] = value;
              return itemMap;
           }, {});
        });
    }
    
    //need to get the values from various places, using a loop here  
    //to make multiple calls
    var itemKeys = ['a', 'b', 'c'];
    itemKeys.forEach(function(key){
        itemsPromise.then(function(data){ 
            return data[key];
        }).then(function(value) {
           console.log('item for key=' + value);
        });
    });
    
    0 讨论(0)
  • 2021-01-03 02:25

    I'm going to add a generic method for caching a promise operation. The trick here is that by treating the promise as a real proxy for a value and caching it and not the value we avoid the race condition. This also works for non promise functions just as well, illustrating how promises capture the concept of async + value well. I think it'd help you understand the problem better:

    function cache(fn){
        var NO_RESULT = {}; // unique, would use Symbol if ES2015-able
        var res = NO_RESULT;
        return function () { // if ES2015, name the function the same as fn
            if(res === NO_RESULT) return (res = fn.apply(this, arguments));
            return res;
        };
    }
    

    This would let you cache any promise (or non promise operation very easily:

     var getItems = cache(getAllItemsFromServer);
    
    
     getItems();
     getItems();
     getItems(); // only one call was made, can `then` to get data.
    

    Note that you cannot make it "synchronous".

    0 讨论(0)
  • 2021-01-03 02:28

    Declare a Flag associative array, and set after the response from server and only query when flags[key] is undefined or null.

    var flags = {}
    itemKeys.forEach(key){
       if(!flags[key]) {
          var value = getItems().then(function(data){ flags[key] = true; return data[key]});
          console.log('item for key=' + value);
       }
    }
    

    may be you should also set flags in few other places depending on the key parameter.

    0 讨论(0)
提交回复
热议问题