Async-Await и ограничение скорости узких мест с помощью Promise.all - PullRequest
0 голосов
/ 01 апреля 2019

Я использую API, который имеет ограничение скорости 500 запросов / мин. Поэтому я решил использовать бутылочное горлышко . Но мне нужно выполнить массив асинхронных функций, который генерирует Promise для выполнения этого вызова API. Я не уверен, что я на правильном пути. Потому что API отвечает мне «Превышен предел скорости 83 за 10_секунд», когда я просто отправляю только 70 запросов за 10 секунд.

Вот как я вызываю основную функцию:

const result = await Helper.updateUsers(request.query.where);
..
..

Вот помощник.js

const Boom = require("boom");
const mongoose = require("mongoose");
const Bottleneck = require("bottleneck");

const Intercom = require("intercom-client");

const config = require("../../config/config");

const client = new Intercom.Client({
  token: config.intercom.access_token
});

const User = mongoose.model("User");
const Shop = mongoose.model("Shop");

// create a rate limiter that allows up to 70 API calls per 10 seconds,
// with max concurrency of 70
const limiter = new Bottleneck({
  maxConcurrent: 70,
  minTime: 10000
});

// Helpers

// This function prepares a valid Intercom User Object.
// user -> User Object
// returns <Promise>
const prepareAndUpdateUser = async user => {
  try {
    let userData = {
      email: user.email,
      user_id: user._id,
      companies: []
    };
    Shop.find({ _id: { $in: user.account.shops } })
      .exec((err, shops) => {
        if (err) console.log("INTERCOM UPDATE USER", err);
        shops.forEach(shop => {
          let shopData = {
            company_id: shop._id,
            name: shop.name[shop.defaultLanguage.code]
          };
          userData.companies.push(shopData);
        });
        // Update Intercom Promise
        return client.users.create(userData);
      });
  } catch (e) {
    return Boom.boomify(err);
  }
};

module.exports.updateUsers = async query => {
  try {
    const users = await User.find(query)
      .populate("account")
      .limit(700);
    if (users && users.length > 0) {
      limiter.schedule(() => {
        const allTasks = users.map(
          async user => await prepareAndUpdateUser(user)
        );
        return Promise.all(allTasks);
      });
      return users.length;
    } else {
      return 0;
    }
  } catch (err) {
    return Boom.boomify(err);
  }
};

Правильно ли я использую Bottleneck & Async-Await?

1 Ответ

1 голос
/ 01 апреля 2019

Первое, на что следует обратить внимание, это использование обратных вызовов в методе async вместо await с обещанием.Вам следует использовать обещание с версией Shops.find() и await результатов.

async function prepareAndUpdateUser(user) {
    try {
        const shops = await Shop.find({ _id: { $in: user.account.shops } }).exec();
        return client.users.create({
            email: user.email,
            user_id: user._id,
            companies: shops.map(shop => {
                return {
                    company_id: shop._id,
                    name: shop.name[shop.defaultLanguage.code]
                };
            })
        });
    } catch (e) {
        return Boom.boomify(err);
    }
}

В вашем методе updateUsers вы используете ограничитель скорости в обратном направлении.Вы хотите отобразить пользователей в ограничитель скорости, чтобы он мог контролировать, когда вызывается prepareAndUpdateUser, в настоящее время вы будете запрашивать все параллельно.Вы также хотите дождаться разрешения, возвращаемого ограничителем скорости.По сути, вы захотите переместить limiter.scehdule(...) в user.map(...).

async function updateUsers(query) {
    try {
        const users = await User.find(query)
            .populate("account")
            .limit(700);
        if (users && users.length > 0) {
            // Schedule an update for each user
            const allTasks = users.map(user => {
                // Schedule returns a promise that resolves when the operation is complete
                return limiter.schedule(() => {
                    // This method is called when the scheduler is ready for it
                    return prepareAndUpdateUser(user)
                })
            });
            // Wait for all the scheduled tasks to complete
            await Promise.all(allTasks);
            return users.length;
        } else {
            return 0;
        }
    } catch (err) {
        return Boom.boomify(err);
    }
}
...