Обнаружение запроса в полете к API в Nodejs

#javascript #node.js #api #asynchronous #async-await

Вопрос:

Я пишу службу в Nodejs, в которой я получаю цены из Api, и вызов API может занять более минуты, поэтому одна из вещей, которые могут произойти, заключается в том, что может произойти запрос на определенный элемент, и один и тот же элемент может быть запрошен до возвращения первого элемента, и я хочу определить, есть ли запрос на указанный элемент в полете, и, если он есть, мне нужно дождаться завершения этого запроса и вернуть один и тот же ответ для обоих запросов.

Примером диаграммы может быть:

 00.000 getCost('123') #1 call 00.001 getExternalCost('123') query 01.000 getCost('123') #2 call 90.001 getExternalCost('123') response 90.002 getCost('123') #1 response 90.003 getCost('123') #2 response  

Это код, который я написал до сих пор, который просто определяет стоимость товара.

 let cache = new Map(); let addToCache = (key,val) =gt; {  if(!cache.has(key)){  cache.set(key,val);  } }  const getCost = async (itemId) =gt; {   if(cache.has(itemId)){  return cache.get(itemId);  }   const price = await getExternalCost(itemId);  addToCache(itemId,price);    return cost; }  

Ответ №1:

Это можно сделать, если вы добавляете обещания в кэш, а не ждете их. Таким образом, входящий запрос может быть перенаправлен для получения значения аналогичного запроса, ожидающего в кэше.

 const MAX_AGE = 1000; const cache = new Map();  const cacheContainsKey = key =gt; {  if (!cache.has(key)) {  return false;  }   const {  _ts  } = cache.get(key);  const age = Date.now() - _ts;  if (age lt;= MAX_AGE) {  return true;  }   console.log(`key ${key} age exceeds MAX_AGE: ${age}. Deleting key from cache.`);  cache.delete(key);  return false; };  const addToCache = (key, val) =gt; {  cache.set(key, {  _ts: Date.now(),  val  }); };  const retrieveFromCache = async(itemId) =gt; {  try {  const start = Date.now();  const price = await cache.get(itemId).val;  const end = Date.now();  console.log(`Query for ${itemId}. Price: ${price}. Time spent: ${(end - start) / 1000} seconds.`);  return price;  } catch (err) {  /*  If required insert logic here to remove the itemId from the cache to  allow new attempts to getExternalCost on this itemId.  */  return err;  } };  const getCost = async(itemId) =gt; {  if (cacheContainsKey(itemId)) {  const price = await retrieveFromCache(itemId);  return price;  }   const pricePromise = getExternalCost(itemId);  addToCache(itemId, pricePromise);   // all queries go through the cache  const price = await retrieveFromCache(itemId);  return price; };   // example code function getExternalCost(itemId) {  return new Promise((resolve, __reject) =gt; {  setTimeout(() =gt; {  resolve(itemId * 100);  }, 1000);  }); }  let counter = 0; const itemIds = [10, 10, 12, 12, 10, 1];  // mimic incoming queries let interval = setInterval(() =gt; {  getCost(itemIds[counter  ]);  if (counter === itemIds.length) {  clearInterval(interval);  } }, 500); 

Комментарии:

1. Да, именно так я видел решение этой проблемы раньше. Кэшируйте обещание, и пусть любой вызывающий абонент, ищущий значение с обещанием, просто использует promise.then() или await promise , чтобы получить значение.

2. Что делать, если первый запрос такого рода возвращает обещание, которое отклоняется (из-за временной ошибки)? Все последующие попадания в кэш также вернут отклоненное обещание.

3. @jsejcksn Это хороший аргумент. Нужно будет что — то настроить для периодической очистки кэша. В качестве альтернативы, некоторая логика может быть добавлена специально для обработки этого в a try/catch для вызова retrieveFromCache . Однако необходимо будет проявить некоторую осторожность, так как это может легко просто ударить по конечной точке, которая выходит из строя и не даст других результатов, которые кэш может помочь защитить. Эта часть полностью зависит от контекста.

4. @jsejcksn Скорректировал ответ, в котором кэш может быть обновлен на основе неудачного запроса.

5. @jsejcksn — Когда обещание отклоняется, вы удаляете его из кэша в тот момент, когда видите, что оно отклонено — сам код кэша также может отслеживать обещание и делать это. Если несколько запросов ожидали этого обещания, все они увидят отказ. Похоже, у тебя не может быть и того, и другого. Либо вы делитесь запросами во время полета, делясь обещаниями и результатами и ошибками, либо вы этого не делаете. Это кажется правильным способом сделать это.