并发异步请求 - 跟踪必要的超时

Concurrent async requests - keep track of necessary timeout

对于 Nominatim 请求,我需要在每个请求之间保持 1500 毫秒的超时。如果多个并发 运行 进程继续触发该函数,我该如何处理?

代码:

osmService:

export const getLocation = async (zipCode, street) => {
  if (!zipCode) {
    return {
      address: null,
      cacheHit: false,
    };
  }

  const cacheData = await getAddress(zipCode, street);

  if (cacheData && cacheData.length === 1) {
    return { address: cacheData[0].address, cacheHit: true };
  } if (cacheData && cacheData.length > 1) {
    logger.error('Found multiple address, this should not be', zipCode + street);
  }


  try {
    const responseObj = await getNominatimLocation(zipCode, street);
    if (responseObj) {
      const returnObj = {
        ...responseObj.address,
        lat: responseObj.lat,
        lon: responseObj.lon,
        displayName: responseObj.display_name,
      };
      await insertAddress(zipCode, street, null, returnObj);
      return {
        address: returnObj,
        cacheHit: false,
      };
    }
    return {
      address: null,
      cacheHit: false,
    };
  } catch (ex) {
    logger.error(`Error getting location from ${zipCode} ${street}`, ex);
  }
  return {
    address: null,
    cacheHit: false,
  };
};

如您所见,我在两者之间有一个缓存层。因此,当请求被缓存命中时,我不需要等待 1500 毫秒。

export const getNominatimLocation = async (zipCode, street, query, retries = 0) => {
  if (retries > 5) {
    return null;
  }
  try {
    const qs = {
      format: 'json',
      q: query,
      postalcode: zipCode,
      addressdetails: 1,
      country: 'Deutschland',
      street,
      'accept-language': 'de',
    };
    const response = await requestPromise()
      .get({
        url: OSM_SEARCH,
        qs,
        timeout: 12000,
        headers: {
          'User-Agent': 'xxxxxxx',
        },
      });
    return JSON.parse(response)[0];
  } catch (ex) {
    logger.info(`Nominatim timed out - retry ${retries}`, ex);
    await timeout(9000);
    return await getNominatimLocation(zipCode, street, query, retries + 1);
  }
};

由于 Nominatim 经常超时,我需要执行此递归调用(不需要递归 - 更容易)。

现在假设我有以下想要获取位置的作业(后端引擎)

const jobA = asnyc () => {
  const {address, cacheHit} = await getLocation(10243);
  if(!cacheHit){
    await timeout(1500)
  }
}

const jobB = asnyc () => {
  const {address, cacheHit} = await getLocation(10245);
  if(!cacheHit){
    await timeout(1500)
  }
}

const startJobs = async () => {
 Promise.all([jobA(),jobB()])
 console.log('all jobs done');
}

作业部分代表了我当前的代码结构。在我的代码中,作业做的更多(调用其他服务等)。

现在,当我有这个布局时 - 当没有 cacheHit 时,我如何确保在每次 Nominatim 调用之间保持 1500 毫秒?

您可以使用每 1.5 秒才解锁一次的锁:

 let lock = Promise.resolve();
 let aquireLock = () => (lock = lock.then(() => new Promise(res => setTimeout(res, 1500))));

然后

await aquireLock();
// will only run every 1.5 seconds