2013-12-12 13 views
11

Wie schreibe ich eine Methode, die Q-Versprechen Nebenläufigkeit begrenzt? Zum Beispiel habe ich eine Methode spawnProcess. Es gibt ein Q-Versprechen zurück.
Ich möchte nicht mehr als 5 Prozess zu einer Zeit, aber transparent für den aufrufenden Code erstellt.Wie kann ich die Parallelität von Q-Versprechen begrenzen?

Was muss ich implementieren eine Funktion mit Unterschrift

function limitConcurrency(promiseFactory, limit) 

, die ich anrufen kann wie

spawnProcess = limitConcurrency(spawnProcess, 5); 

// use spawnProcess as usual 

begann ich schon auf meiner Version arbeiten, aber ich frage mich, ob jemand eine kurze Einführung hat gegen die ich nachprüfen kann.

+0

Sie Code für den Browser zu schreiben sind, oder für Knoten? Wenn es der ehemalige ist, gibt es keine Nebenläufigkeit ... –

+0

@Matt: Ich schreibe für den Knoten. Ich meine nicht Nebenläufigkeit wie im Threading, ich meine Nebenläufigkeit wie in "Versprechungen, die zur gleichen Zeit laufen". –

+0

Was hast du probiert? Die Verwendung einer Warteschlange und die Pufferung von Anfragen sollten nicht zu schwer sein. – schlingel

Antwort

10

Ich habe eine Bibliothek, die dies für Sie tun https://github.com/ForbesLindesay/throat

Sie es über browserify verwenden können oder das Standalone-Build von brcdn (https://www.brcdn.org/?module=throat&version=latest) herunterladen und als Script-Tag hinzufügen.

Dann (vorausgesetzt, der Promise Konstruktor in Ihrer Umgebung polyfilled oder implementiert) Sie tun können:

//remove this line if using standalone build 
var throat = require('throat'); 

function limitConcurrency(promiseFactory, limit) { 
    var fn = throat(promiseFactory, limit); 
    return function() { 
    return Q(fn.apply(this, arguments)); 
    } 
} 

Sie nur throat(promiseFactory, limit) direkt anrufen können, aber das wäre eine Rückkehr promise Versprechen eher als ein Q Versprechen.

Ich mag es auch wirklich, es mit array.map zu verwenden.

// only allow 3 parallel downloads 
var downloadedItems = Q.all(items.map(throat(download, 3))); 
+0

Das ist süß, danke! Ihre Bibliothek sieht aus wie idiomatische JS als meine. Auch Tests. –

+0

Froh, dass Sie es nützlich finden :) – ForbesLindesay

2

Das scheint für mich zu arbeiten.

Ich bin mir nicht sicher, ob ich es vereinfachen könnte. Die Rekursion in scheduleNextJob ist notwendig, damit die running < limit und limit++ immer im selben Tick ausgeführt werden.

Also available as a gist.

'use strict'; 

var Q = require('q'); 

/** 
* Constructs a function that proxies to promiseFactory 
* limiting the count of promises that can run simultaneously. 
* @param promiseFactory function that returns promises. 
* @param limit how many promises are allowed to be running at the same time. 
* @returns function that returns a promise that eventually proxies to promiseFactory. 
*/ 
function limitConcurrency(promiseFactory, limit) { 
    var running = 0, 
     semaphore; 

    function scheduleNextJob() { 
    if (running < limit) { 
     running++; 
     return Q(); 
    } 

    if (!semaphore) { 
     semaphore = Q.defer(); 
    } 

    return semaphore.promise 
     .finally(scheduleNextJob); 
    } 

    function processScheduledJobs() { 
    running--; 

    if (semaphore && running < limit) { 
     semaphore.resolve(); 
     semaphore = null; 
    } 
    } 

    return function() { 
    var args = arguments; 

    function runJob() { 
     return promiseFactory.apply(this, args); 
    } 

    return scheduleNextJob() 
     .then(runJob) 
     .finally(processScheduledJobs); 
    }; 
} 

module.exports = { 
    limitConcurrency: limitConcurrency 
} 
2

Die Deferred Versprechen Implementierung hat gate Funktion, die genau auf diese Weise funktioniert:

spawnProcess = deferred.gate(spawnProcess, 5);  
2

ich eine kleine Bibliothek geschrieben, dies zu tun: https://github.com/suprememoocow/qlimit

Es ist extrem einfach zu bedienen und wurde speziell entwickelt, mit Q Versprechen zu arbeiten:

var qlimit = require('qlimit'); 
var limit = qlimit(2); // 2 being the maximum concurrency 

// Using the same example as above 
return Q.all(items.map(limit(function(item, index, collection) { 
    return performOperationOnItem(item); 
})); 

Es kann auch die Parallelität zu einer bestimmten Ressource zu begrenzen verwendet werden, wie folgt aus:

var qlimit = require('qlimit'); 
var limit = qlimit(2); // 2 being the maximum concurrency 

var fetchSomethingFromEasilyOverwhelmedBackendServer = limit(function(id) { 
    // Emulating the backend service 
    return Q.delay(1000) 
    .thenResolve({ hello: 'world' }); 
}); 
Verwandte Themen