2016-02-15 3 views
3

Я как бы новичок в узле, но я понимаю, что писать синхронные функции - это плохо. Я блокирую цикл событий или что-то в этом роде ... Так что хорошо писать все асинхронно.Как ограничить/контролировать слишком много асинхронных вызовов?

Но в некоторых случаях писать все асинхронно может быть плохо. Например, у меня есть функция, которая вызывает вызов API (для сторонней службы API), а затем я должен записать результат в базу данных. Мне нужно сделать это в течение короткого промежутка времени, например 500 раз.

Вызов этого API 500 раз асинхронно, а затем запись в базу данных 500 раз асинхронно, вероятно, запретит мне службу API (дросселирование) и перегрузить мой сервер базы данных.

Каков наилучший способ контроля или ограничения чего-либо подобного? Я хочу держать вещи асинхронными, так что они эффективны, но я просто не могу использовать вышеупомянутый подход.

Я исследовал около Promise throttling подходов. Это правильный способ подойти к этой проблеме? Есть ли более подходящий способ сделать это?

+0

Посмотрите на пакет 'async' npm - в частности, на очередь: https://github.com/caolan/async#queue – TMan

+1

Вам нужно будет ограничить лимит самостоятельно, чтобы не переусердствовать с сервисом API , На NPM есть модули, которые помогут вам это сделать, или вы можете просто закодировать свои собственные решения. Если вы делаете это самостоятельно, вам просто нужно сохранить счетчики и отметки времени, чтобы вы могли отслеживать, сколько вызовов API было сделано за последние N секунд, и, возможно, создать очередь команд, ожидающих выхода, когда временной интервал открывается. Подробные сведения о том, как это сделать, во многом зависят от ваших данных и правил, которые вы пытаетесь использовать в API. – jfriend00

+0

[Это хороший JSFiddle] (http://jsfiddle.net/dukeytoo/02ohnth4/) с некоторыми хорошими шаблонами для циклических асинхронных вызовов и их можно использовать для ограничения/уменьшения количества запросов! –

ответ

-1

Один простой способ ограничить это - использовать setTimeout и выполнить «рекурсивный» цикл, подобный этому.

function asyncLoop() { 
    makeAPICall(function(result) { 
    writeToDataBase(result, function() { 
     setTimeout(asyncLoop, 1000); 
    }); 
    }); 
} 

И, конечно, вы также можете использовать ту же стратегию с обещаниями.

+0

Интересный подход. У меня такое чувство, что обезвреживающие библиотеки и, возможно, делают это за кулисами ... Есть ли недостатки в этом? –

+0

Единственная проблема заключается в том, что setTimeout имеет минимальное время ожидания.Эта сумма может составлять несколько мс. – SpiderPig

1

Я не знаю, как Promise throttle работает под капотом, я считаю Promise лучший подход по сравнению с setTimeout, с обещанием больше событий на основе, моя проблема с этим пакетом НПМ не, он предлагает не вариант обратного вызова, как только ваш вызов сделано, моя бы реализация внешнего что-то вроде:

class PromiseThrottler { 
 
    constructor(maxParallelCalls) { 
 
     this.maxParallelCalls = maxParallelCalls; 
 
     this.currentCalls = 0; // flag holding the no. of parallel calls at any point 
 
     this.queue = []; // queue maintaining the waiting calls 
 
    } 
 
    // pormiseFn - the fn that wraps some promise call the we need to make, thenChain - callback once your async call is done, args- arguments that needs to be passed to the function 
 
    add(promiseFn, thenChain, ...args) { 
 
    this.queue.push({ 
 
     promiseFn, thenChain, args 
 
    }); 
 
    this.call(); 
 
    } 
 
    call() { 
 
    if (!this.queue.length || this.currentCalls >= this.maxParallelCalls) return; 
 
    this.currentCalls++; 
 
    let obj = this.queue.shift(); 
 
    let chain = obj.args.length ? obj.promiseFn(...obj.args) : obj.promiseFn(); 
 
    if (obj.thenChain) chain.then(obj.thenChain); 
 
    chain 
 
     .catch(() => {}) 
 
     .then(() => { 
 
     this.currentCalls--; 
 
     this.call(); 
 
     }); 
 
    this.call(); 
 
    } 
 
} 
 

 
//usage 
 

 
let PT = new PromiseThrottler(50) 
 
    , rn = max => Math.floor(Math.random() * max) // generate Random number 
 
    , randomPromise = id => new Promise(resolve => setTimeout(() => resolve(id), rn(5000))) // random promise generating function 
 
    , i = 1 
 
    , thenCall = id => { 
 
    console.log('resolved for id:', id); 
 
    let div = document.createElement('div'); 
 
    div.textContent = `resolved for id: ${id}`; 
 
    document.body.appendChild(div); 
 
    }; 
 

 
while (++i < 501) PT.add(randomPromise, thenCall, i);

1

async НОГО пакета является прекрасным и имеет несколько решений, которые могут быть использованы в данной конкретной ситуации. Один подход заключается в использовании queue с пределом набора параллелизмом (пример, взятый непосредственно из async README):

// create a queue object with concurrency 2 
var q = async.queue(function (task, callback) { 
    console.log('hello ' + task.name); 
    callback(); 
}, 2); 

// assign a callback 
q.drain = function() { 
    console.log('all items have been processed'); 
} 

// add some items to the queue 
q.push({name: 'foo'}, function (err) { 
    console.log('finished processing foo'); 
}); 

github.com/caolan/async#queue

В вашей конкретной ситуации, просто ждать, чтобы вызвать callback() до любой временной или транзакции детализации ждут завершено.