加快执行多个异步调用的速度。

问题描述 投票:1回答:1

我不能分享我的具体代码--但我试图在下面说明我的情况。

远程API的最大请求限制为10 requestspér sec.,我想加快我的代码以接近这个限制。目前的代码是每秒钟运行1-2个请求。

例子 - 为100个不同的人获取10小时的数据。

(async function get(...) {
    await getPersonData(for one person);
    if (not all persons' data has been fetched) { get(fetch data for the next person); }
})(...);

async function getPersonData() {
    const personData = await getHistoricalData(...);
    ...
};

async function getHistoricalData(...) {

    // Fetch 10 hours of data ...

    while (as long as all data has not yet been fetch...) {
        const data = await getOneHourOfData(...);
        ...
    }
    return all_20_hours_of_data;
} 

async function getOneHourOfData(...) {
    return await remote.api.getData(get 1 hour of data);
}

上面的例子是我的标准版代码--我也尝试了两种不同的方法。

  • 使用Promise.all(),同时获取5个人的数据。
  • 复制并同时运行多个版本的get()函数(前4行代码块)

两种方法都有效--但似乎都没有加快任何速度......??我有一个想法,是 while-loop 阻碍了整个过程的速度?

javascript node.js performance parallel-processing async-await
1个回答
2
投票

你的问题中的代码实际上是这样的。

(async function get() {
    try {
        console.time("get");
        console.log(JSON.stringify(await getPersonData()));
        console.timeEnd("get");
    } catch (e) {
        console.error(e);
    }
})();

async function getPersonData() {
    const personData = await getHistoricalData();
    return personData;
};

async function getHistoricalData() {
    const data = [];
    for (let hour = 0; hour < 10; ++hour) {
        data.push(await getOneHourOfData());
    }
    return data;
} 

function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

let num = 0;
async function getOneHourOfData() {
    await delay(150);
    return ++num;
}

它需要大约1500毫秒的时间来运行

下面是同样的事情,并行地做10个 "小时 "的调用,并使用 Promise.all:

(async function get() {
    try {
        console.time("get");
        console.log(JSON.stringify(await getPersonData()));
        console.timeEnd("get");
    } catch (e) {
        console.error(e);
    }
})();

async function getPersonData() {
    const personData = await getHistoricalData();
    return personData;
};

async function getHistoricalData() {
    const promises = [];
    for (let hour = 0; hour < 10; ++hour) {
        promises.push(getOneHourOfData()); // <== No `await`!
    }
    return Promise.all(promises); // <== `await `on this line is optional but
                                  // pointless, this is an `async`
                                  // function, so its promise will be
                                  // resolved to the promise we return
} 

function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

let num = 0;
async function getOneHourOfData() {
    await delay(150);
    return ++num;
}

它的运行时间大约是150ms,因为10次历史数据的调用是并行发生的。请注意,最关键的是要建立一个承诺数组(不需要 awaiting他们),然后用 Promise.all 来获取整个承诺数组的单一承诺。


1
投票

你可以使用一个(非常特殊的)Semaphore来将API调用限制在一定的速率内。

 class TimeSemaphore {
   #times = []; 
   #backlog = Promise.resolve();

   constructor(interval, parallel) {
     this.interval = interval; this.parallel = parallel;
   }

   async aquire(cb) {
     this.#backlog = this.#backlog.then(() => {
      if(this.#times.length >= this.parallel && Date.now() - this.#times[0] < this.interval)
         return new Promise(res => setTimeout(res, this.interval - (Date.now() - this.#times[0]));
     });

    this.#times.push(Date.now());

    await this.#backlog;

    try {
      return await cb();
    } finally {
      this.#times.shift();
    }
  }
 }

这可以作为:

  const apiLimit = new TimeSemaphore(1000, 5);

  async function callAPI() {
    await apiLimit.aquire(async function() {
      await fetch(...);
     });
  }

  callAPI(); // as often as you want

0
投票

我喜欢使用 Promise.all.

const data = await Promise.all([
  getOneHourOfData(params)
  ... // the same as above but different params x times
])

现在,我非常好奇的是 while (as long as all data has not yet been fetch...) { 表情.也许是

await new Promise((resolve, reject) => setTimeout(resolve, 1000))

?

© www.soinside.com 2019 - 2024. All rights reserved.