2016-08-04 21 views
5

Ich bin auf der Suche nach einem Versprechen Funktion Wrapper, die begrenzen/Drosselung, wenn eine gegebene Versprechen ausgeführt wird, so dass nur eine bestimmte Anzahl der Versprechen zu einem bestimmten Zeitpunkt ausgeführt wird.Limit Concurrency des Versprechens ausgeführt

In dem folgenden Fall sollte delayPromise nie gleichzeitig ausgeführt werden, sie sollten alle nacheinander in der Reihenfolge "Wer zuerst kommt, mahlt zuerst" ausgeführt werden.

import Promise from 'bluebird' 

function _delayPromise (seconds, str) { 
    console.log(str) 
    return Promise.delay(seconds) 
} 

let delayPromise = limitConcurrency(_delayPromise, 1) 

async function a() { 
    await delayPromise(100, "a:a") 
    await delayPromise(100, "a:b") 
    await delayPromise(100, "a:c") 
} 

async function b() { 
    await delayPromise(100, "b:a") 
    await delayPromise(100, "b:b") 
    await delayPromise(100, "b:c") 
} 

a().then(() => console.log('done')) 

b().then(() => console.log('done')) 

Irgendwelche Ideen, wie man eine Warteschlange wie diese einrichten?

Ich habe eine "Entprellen" -Funktion von der wunderbaren Benjamin Gruenbaum. Ich muss dies ändern, um ein Versprechen zu drosseln, das auf seiner eigenen Ausführung und nicht auf der Verzögerung basiert.

export function promiseDebounce (fn, delay, count) { 
    let working = 0 
    let queue = [] 
    function work() { 
    if ((queue.length === 0) || (working === count)) return 
    working++ 
    Promise.delay(delay).tap(function() { working-- }).then(work) 
    var next = queue.shift() 
    next[2](fn.apply(next[0], next[1])) 
    } 
    return function debounced() { 
    var args = arguments 
    return new Promise(function (resolve) { 
     queue.push([this, args, resolve]) 
     if (working < count) work() 
    }.bind(this)) 
    } 
} 
+0

async.js und queue.js unterstützen beide konfigurierbare Parallelität. – nrabinowitz

+0

Für Arrays oder die Verwaltung des Zustands einer bestimmten Funktion und ihrer Instanzen? – ThomasReggi

+0

@rabinowitz beide dieser Bibliotheken haben nichts mit Versprechungen zu tun. – ThomasReggi

Antwort

3

Ich glaube nicht, gibt es keine Bibliotheken, dies zu tun, aber es ist eigentlich ganz einfach selbst implementieren:

function queue(fn) { // limitConcurrency(fn, 1) 
    var q = Promise.resolve(); 
    return function(x) { 
     var p = q.then(function() { 
      return fn(x); 
     }); 
     q = p.reflect(); 
     return p; 
    }; 
} 

Für mehrere gleichzeitige Anfragen es ein wenig schwieriger wird, kann aber getan werden auch.

function limitConcurrency(fn, n) { 
    if (n == 1) return queue(fn); // optimisation 
    var q = null; 
    var active = []; 
    function next(x) { 
     return function() { 
      var p = fn(x) 
      active.push(p.reflect().then(function() { 
       active.splice(active.indexOf(p), 1); 
      }) 
      return [Promise.race(active), p]; 
     } 
    } 
    function fst(t) { 
     return t[0]; 
    } 
    function snd(t) { 
     return t[1]; 
    } 
    return function(x) { 
     var put = next(x) 
     if (active.length < n) { 
      var r = put() 
      q = fst(t); 
      return snd(t); 
     } else { 
      var r = q.then(put); 
      q = r.then(fst); 
      return r.then(snd) 
     } 
    }; 
} 

Btw, können Sie einen Blick auf die actors model und CSP haben wollen. Sie können den Umgang mit solchen Dingen vereinfachen, es gibt auch einige JS-Bibliotheken für sie.

Beispiel

import Promise from 'bluebird' 

function sequential(fn) { 
    var q = Promise.resolve(); 
    return (...args) => { 
    const p = q.then(() => fn(...args)) 
    q = p.reflect() 
    return p 
    } 
} 

async function _delayPromise (seconds, str) { 
    console.log(`${str} started`) 
    await Promise.delay(seconds) 
    console.log(`${str} ended`) 
    return str 
} 

let delayPromise = sequential(_delayPromise) 

async function a() { 
    await delayPromise(100, "a:a") 
    await delayPromise(200, "a:b") 
    await delayPromise(300, "a:c") 
} 

async function b() { 
    await delayPromise(400, "b:a") 
    await delayPromise(500, "b:b") 
    await delayPromise(600, "b:c") 
} 

a().then(() => console.log('done')) 
b().then(() => console.log('done')) 

// --> with sequential() 

// $ babel-node test/t.js 
// a:a started 
// a:a ended 
// b:a started 
// b:a ended 
// a:b started 
// a:b ended 
// b:b started 
// b:b ended 
// a:c started 
// a:c ended 
// b:c started 
// done 
// b:c ended 
// done 

// --> without calling sequential() 

// $ babel-node test/t.js 
// a:a started 
// b:a started 
// a:a ended 
// a:b started 
// a:b ended 
// a:c started 
// b:a ended 
// b:b started 
// a:c ended 
// done 
// b:b ended 
// b:c started 
// b:c ended 
// done 
+0

@ThomasReggi: so das Beispiel funktioniert, gibt es noch etwas, das Sie wollen beantwortet? – Bergi

+0

Hm, jemand ist auf einer Downvote-Tour, ohne ihre Argumentation zu erklären. – Bergi

-1

Mit dem gedrosselten-Versprechen Modul:

https://www.npmjs.com/package/throttled-promise

var ThrottledPromise = require('throttled-promise'), 
    promises = [ 
     new ThrottledPromise(function(resolve, reject) { ... }), 
     new ThrottledPromise(function(resolve, reject) { ... }), 
     new ThrottledPromise(function(resolve, reject) { ... }) 
    ]; 

// Run promises, but only 2 parallel 
ThrottledPromise.all(promises, 2) 
.then(...) 
.catch(...); 
+1

Ich sehe nicht, wie das Problem des OP, das Versprechen in verschiedenen Bereichen schafft, mit dieser Bibliothek gelöst werden könnte. Können Sie bitte die 'a' und' b' Funktionen oder 'limitConcurrency' implementieren? – Bergi

+1

Dies ist eine populäre Bibliothek, die ganz praktisch die Frage von op anspricht: "Ich suche nach einem Funktionswrapper, der limitieren/drosseln kann, wenn ein bestimmtes Versprechen läuft, so dass nur eine bestimmte Anzahl dieser Versprechen zu einem bestimmten Zeitpunkt ausgeführt wird . " Wenn Sie versuchen, Versprechen zu drosseln, sollten Sie throttled-promise verwenden. SO ist für die Beantwortung von Fragen, nicht Code auf Anfrage zu schreiben. – hunterloftis

+1

Er sucht nach einem Wrapper mit Versprechen zurückgebenden Funktionen, nicht einem eingepackten 'Promise'-Konstruktor. Auch diese Bibliothek scheint zu verlangen, dass Sie 'ThrottledPromise.all' verwenden, was nicht zu dem in der Frage angegebenen Problem passt. Bitte versuchen Sie, das 'a' /' b' Beispiel zu implementieren und Sie werden wahrscheinlich sehen, wie es nicht funktioniert - oder wenn es funktioniert, beantwortet dieser Code tatsächlich die Frage. Beachten Sie, dass SO kein Bibliotheksempfehlungsdienst ist, sondern tatsächliche Antworten auf die Fragen erfordert, bei denen Code geschrieben wird. – Bergi

-2

Der klassische Weg Asynchron-Prozesse in Reihe läuft async.js und async.series() verwenden zu verwenden. Wenn Sie versprechen basierten Code bevorzugen, dann ist es ein Versprechen Version async.js: async-q

Mit async-q können Sie noch einmal verwenden series:

async.series([ 
    function(){return delayPromise(100, "a:a")}, 
    function(){return delayPromise(100, "a:b")}, 
    function(){return delayPromise(100, "a:c")} 
]) 
.then(function(){ 
    console.log(done); 
}); 

Rennen zwei von ihnen zur gleichen Zeit wird a und b gleichzeitig laufen aber in jedem werden sie sequentiell sein:

// these two will run concurrently but each will run 
// their array of functions sequentially: 
async.series(a_array).then(()=>console.log('a done')); 
async.series(b_array).then(()=>console.log('b done')); 

Wenn Sie b nachausführen möchtenlegte sie dann in der .then():

async.series(a_array) 
.then(()=>{ 
    console.log('a done'); 
    return async.series(b_array); 
}) 
.then(()=>{ 
    console.log('b done'); 
}); 

Wenn stattdessen jeder der Reihe nach laufen Sie jede begrenzen wollen gleichzeitig eine bestimmte Anzahl von Prozessen ausgeführt werden, dann können Sie parallelLimit() verwenden:

// Run two promises at a time: 
async.parallelLimit(a_array,2) 
.then(()=>console.log('done')); 

Informieren Sie sich die async-q Dokumente: https://github.com/dbushong/async-q/blob/master/READJSME.md

+0

OP möchte nicht wissen, wie man 'a' /' b' ohne das async-Schlüsselwort implementiert, sondern wie 'limitConcurrency' implementiert wird, so dass im Beispiel die 'delayPromise' Aufrufe von' a' und 'b' nicht parallel sind . – Bergi

+0

@Bergi: Das habe ich gezeigt. Mit 'async.series()' wird 'delayPromise' nacheinander ausgeführt. – slebetman

+1

Nein. Sie sollten sequentiell sein, auch wenn Sie 'a' und' b' gleichzeitig aufrufen. – Bergi

0

Ich habe das gleiche Problem. Ich habe eine Bibliothek geschrieben, um es zu implementieren.Der Code lautet here. Ich habe eine Warteschlange erstellt, um alle Versprechen zu speichern. Wenn Sie einige Versprechen in die Warteschlange schieben, werden die ersten Versprechen an der Spitze der Warteschlange ausgelöst und ausgeführt. Sobald ein Versprechen abgeschlossen ist, wird auch das nächste Versprechen in der Warteschlange angezeigt und ausgeführt. Immer wieder, bis die Warteschlange keine Task hat. Sie können den Code für Details überprüfen. Hoffe, diese Bibliothek würde dir helfen.