Home > Back-end >  Promises vs Loop in JavaScript: how to avoid Too Many Requests and run simultaneously
Promises vs Loop in JavaScript: how to avoid Too Many Requests and run simultaneously

Time:01-07

On past few days I am trying to solve a situation involving API requests and Promises. First of all, I am using Axios-rate-limit to manage requests at maximum of 30 per minute.

const requestTiny = axiosRateLimit(axios.create({'baseURL': '*****'}), {maxRequests: 30, perMilliseconds: 60500})

The main function is syncProducts as follows:

export const syncProducts = async (req, res) => {
    let productsBN = []
    let products = await getProductsERP('BN')
    for (let prod of products) {
        const product = await getProductERP(prod.store, prod.id)
        productsBN.push(product)
    }
    res.status(201).json(productsBN)
}

getProductsERP returns IDs and then getProductERP returns details of each product. BN is the store. This code does the job. I do not get Too Many Requests errors.

But now I need to grab data from multiple stores. The API request limit is per store. So, run stores in parallel is much faster.

First attempt was duplicate some lines for store ORL. As expected, code runs line by line and second store has to wait first store to finish.

export const syncProducts = async (req, res) => {
    let productsORL = [], productsBN = []
    let prodsBN = await getProductsERP('BN')
    for (let prod of prodsBN) {
        const product = await getProductERP(prod.store, prod.id)
        productsBN.push(product)
    }
    let prodsORL = await getProductsERP('ORL')
    for (let prod of prodsORL) {
        const product = await getProductERP(prod.store, prod.id)
        productsORL.push(product)
    }
    res.status(201).json(productsBN.concat(productsORL))
}

Based on this I tried to implement the same logic but using Promises.

export const syncProducts = async (req, res) => {
    let productsORL = [], productsBN = []
    Promise.all([
        getProductsERP('ORL').then(async products => {
            productsORL = await Promise.all(products.map(async prod => await getProductERP(prod.store, prod.id)))
        }),
        getProductsERP('BN').then(async products => {
            productsBN = await Promise.all(products.map(async prod => await getProductERP(prod.store, prod.id)))
        })
    ])
    .then(res.status(201).json(productsBN.concat(productsORL)))
    .catch(error => res.status(409).json(error))
}

The problem is while getProductsERP is running, Axios-rate-limit works well and both stores run in parallel (desired behavior). When getProductERP runs, it starts all the requests together, causing Too Many Requests error.

What am I doing wrong? How to make getProductERP promises to respect API limits?

PS: I tried to resume as much as possible. If I am missing some important information to evaluate solution, just let me know and I will update the code. Thanks,

CodePudding user response:

I think this is what you're going for.

// Gets products for one store, sequentially.
async function getProducts(storeCode) {
  const products = [];
  for (const {store, id} of await getProductsERP(storeCode)) {
    products.push(await getProductERP(store, id));
  }
  return products;
}

// Gets products for multiple stores in parallel.
export const syncProducts = async (req, res) => {
    const products = await Promise.all(getProducts('BN'), getProducts('ORL'));
    res.status(201).json(products.flat());
}
  • Related