danielnewman
@danielnewman
Front-end

Как послать 10000 запросов через setTimeout в Node.JS с адекватным расходом памяти?

Есть табличный сервис Airtable (а-ля Excel), с возможностью записи строк в таблицу через API. Нужно вкатить в этот сервис построчно 10 000 строк из коллекции Data. Есть ограничения API в 10 запросов/сек. Я выполняю это размещение через конструкцию вида (setTimeout/setInterval):

Data.forEach(function(item,index){
    setTimeout(function(){
        API.sendData(item);
    });
});

Проблема в том, как я это понимаю, что forEach быстро отрабатывает всю коллекцию и заталкивает в память эту чреду функций (или еще каким-то образом её планомерно и полностью выедает). В итоге мне не хватает для передачи 20Мб базы данных в 10000 записях 1.5Gb имеющейся серверной памяти и приложение падает и/или не отрабатывает коллекцию, это тоже не очень понятно: чья тут вина - моя или API дает сверху timeout и мои запросы висят дольше.

Мне кажется, что вышеприведенная конструкция просто не подходит для такого рода задачи и, наверняка, вы с подобным могли столкнуться в своем опыте. Нужен совет и настовление. Спасибо.

UPD: Код API и ответ сервера

Code
var Airtable = require('airtable');
var app = new Airtable({ apiKey: 'YOUR_API_KEY' }).app('appMzaconT6Iw9A3g');

app('Categories').create({
  "Category name": "Social Innovation",
  "Number of Orgs.": " 59",
  "CatID": "bf5c955e6f68da36e5021d3962c28325",
  "Last update": "2015-03-11T09:08:44.000Z"
}, function(err, records, newOffset) {
    if (err) { console.log(err); return; }
    records.forEach(function(record) {
        console.log(record);
    });
});

Output
{
    "id": "rec00GC0bR7Eh1bGv",
    "fields": {
        "Category name": "Social Innovation",
        "Number of Orgs.": " 59",
        "CatID": "bf5c955e6f68da36e5021d3962c28325",
        "Last update": "2015-03-11T09:08:44.000Z"
    }
}
  • Вопрос задан
  • 862 просмотра
Решения вопроса 1
Lynn
@Lynn
nginx, js, css
Я бы сделал примерно так:
var l = Data.length;
var n = 0;

function send() {
  API.sendData(Data[n]);
  n++;
  if (n < l) {
    setTimeout(send, 100);
  }
}

send();


Смысл прост, не ставим в очередь сразу все 10000 запросов, а добавляем в очередь по одному после выполнения предыдущего. Тогда у вас в ожидании всегда будет только один запрос.
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
smanioso
@smanioso
Отмечайте ответы на свои вопросы!
var i = 0;
var data = [];
function foo() {
  if (i == data.length) 
      return;
  // send one item to airtable
  i++;
  setTimeout(foo, 1000);
}


+ можно открыть issue для их собственной библиотеки https://github.com/airtable/airtable.js
Ответ написан
@shedy2
Думаю, что логичнее всего использовать async

async.eachLimit(records, 8, function(record, async_cb) {
   /// your code
   async_cb();
}, function() {
 console.log('done!');
});


Будет делать 8 запросов параллельно, по завершении предыдущего будет запускаться новый запрос
Ответ написан
Комментировать
@avdosev
я понимаю что вопрос старый, но мне кажется что такой вариант более простой

function sleep(time) {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, time)
    })
}


а вот пример использования

async function cycle() {
    const timeToSleep = 1000
    console.log('start')
    await sleep(timeToSleep)
    for (const value of [0,1,2,3,4,5,6]) {
        console.log(value)
        await sleep(timeToSleep)
    }
    console.log('stop')
}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы