2015-07-29 5 views
0

Я бы сделал один звонок на сервер, чтобы получить список элементов. Как убедиться, что выполняется только один вызов, и коллекции обрабатываются только один раз, чтобы создать карту значений ключей.Кэширование результатов обещания JavaScript

var itemMap = {}; 

function getItems(){ 
    getAllItemsFromServer().then(function(data){ 
    data.forEach(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(key){ 
    var value = getItems().then(function(data){ return data[key]}); 
    console.log('item for key=' + value); 
} 
+0

Просто проверьте itemmap, прежде чем сделать звонок, если длина> 0 просто возвращает itemMap – thllbrg

+2

Caché обещание, а не данные, возвращать _that_. –

ответ

3

Я собираюсь добавить общий метод для кеширования операции обещания. Фокус в том, что, рассматривая обещание как реальный прокси для ценности и кеширования, а не ценность, мы избегаем условия гонки. Это также работает для не обедневных функций так же хорошо, что иллюстрирует, как обещания хорошо воспринимают концепцию async + value. Я думаю, что бы помочь вам понять проблему лучше:

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; 
    }; 
} 

Это позволит вам кэшировать любые обещания (или несрабатывание обещание очень легко:

var getItems = cache(getAllItemsFromServer); 


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

Обратите внимание, что вы не можете make it "synchronous"

+0

Я никогда не думал о том, чтобы использовать Символы как токены, для этого всегда была достаточно идентичность объекта. Умная! – Bergi

+0

Отлично. Я пытался что-то подобное с некоторой дополнительной сложностью. Позвольте мне проверить, очистив мой дополнительный материал. – dipu

+0

Это не кэширование с аргументами как право вправо? Было бы легко изменить его, чтобы сделать это? – Basil

0

Объявите флаг ассоциативный массив, и устанавливается после ответа от сервера и только запрос, когда флаги [ключ] не определен или нуль.

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); 
    } 
} 

Возможно, вы также должны установить флаги в нескольких других местах в зависимости от параметра ключа.

+1

flags [key] будет установлен в true только после того, как ответ будет получен от сервера. Это означает, что на сервер будет отправлено несколько вызовов. – dipu

+0

@dipu Я добавил ответ, трюк заключается в том, чтобы скрыть обещание, а не значение. –

3

Обещание является statefull, и как только оно будет выполнено, его значение не может быть изменено. Вы можете использовать .then несколько раз, чтобы получить его содержимое, и каждый раз получите тот же результат.

GetAllItemsFromServer возвращает обещание, тогда блок манипулирует ответами и возвращает itemMap, который завернут в ответ (цепочка обещаний). Обещание затем кэшируется и может использоваться для повторного получения itemMap.

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

Это был очень полезный комментарий, спасибо. – Vincent

2
.

Я думаю, что вы действительно ищете является

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! 
}); 
+1

У нас действительно должна быть система: P –

+1

@BenjaminGruenbaum Вот что я только подумал по другому вопросу ... – Bergi

+1

Это полностью спасло бы нас наполовину. –

0

Try НПМ dedup-async, который кэширует дублированные обещания, если есть ожидающий, так что одновременное обещание вызывает только одну действительную задачу.

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