提问者:小点点

异步生成反向地理编码地址列表,无需API速率限制


我正在用node/mongo/mongoose生成一堆数据,并希望为每个项调用反向地理代码API,但由于来自外部API的请求太多,我要用到429。 我尝试使用lodash.delay()在内部强制等待每个请求,但没有成功。 理想情况下,我还希望分叉子进程,以免阻塞脚本/进程。 (虽然这是为了开发目的,所以我不在乎)。

messageSchema.pre("insertMany", async function save(next, docs) {
      docs.map(async (doc) => {
        [err, response] = await to(
          delay(
            reverseGeocode(
              doc.location.coordinates[0],
              doc.location.coordinates[1]
            ),
            3000
          )
        );
        if (err) {
          throw new Error(err);
        }
      });
      console.log(this);
    });

共2个答案

匿名用户

由于NodeJS具有本机异步承诺支持,因此不需要“分叉”进程。

最好的解决方案实际上是在您的代码中添加延迟以匹配限制。 假设这太麻烦了,而且这仅仅是为了开发目的,这里是一个快速的蛮力示例:

请注意,等待承诺不会像同步代码那样阻塞进程。

async function getGeoCode(doc) {
    return reverseGeocode(
        doc.location.coordinates[0],
        doc.location.coordinates[1]
    )
}

const randomSleep = [1000, 2000, 3000, 4000, 5000, 6000];

messageSchema.pre("insertMany", async function save(next, docs) {
    let sleep = false;
    docs.map(async (doc) => {
        
        
        while (sleep) {
            const randomSleep = randomSleep[Math.floor(Math.random() * randomSleep.length)];

            await new Promise(function (resolve) {
                setTimeout(resolve, 2000 + randomSleep)
            });
        }
        
        let [err, response] = await getGeoCode(doc);
        if (err) {
            if (err.statusCode !== 429) {
                throw new Error(err);
            }

            sleep = true;

            while (err && err.statusCode === 429) {
                const randomSleep = randomSleep[Math.floor(Math.random() * randomSleep.length)];

                await new Promise(function (resolve) {
                    setTimeout(resolve, 2000 + randomSleep)
                });
                
                [err, response] = await getGeoCode(doc);
            }
            sleep = false;
        }

    });
    console.log(this);
});

匿名用户

我使用这个库来限制请求。 您只需告诉它API的速率限制是多少,您就可以任意快速地调用它,它将自动地为您间隔一段时间的请求。

如果您不想要另一个依赖项,那么为了使您的解决方案工作,您需要使用for循环。 map将始终尽可能快地执行。

const wait = (time) => {
  return new Promise((resolve) => {
    setTimeout(resolve, time);
  });
}

messageSchema.pre("insertMany", async function(next, docs) {
  for(let i in docs) {
    const doc = docs[i];
    await wait(3000); // milliseconds to space requests out by.
    const [err, response] = await to(
      // You probably need to adjust this since you didn't tell us
      // what your reverse geocode function actually returns.
      reverseGeocode(
        doc.location.coordinates[0],
        doc.location.coordinates[1]
      )
    );
    if (err) {
      throw new Error(err);
    }
  }
  console.log(this);
});

这可能有点错误,因为我不确定toreverseGeocode函数实际做什么。 如果你给我更多关于你的代码如何工作的信息,我可以给你一个确切的答案。 reverseGeocode只是构建请求参数并使用to发出请求吗? 或者reverseGeocode是异步的?

相关问题