javascript - Limite la concurrencia de la promesa que se ejecuta
node.js promise (4)
Estoy buscando un contenedor de funciones de promesa que pueda limitar / acelerar cuando se ejecuta una promesa determinada, de modo que solo se ejecute un número determinado de esa promesa en un momento dado.
En el siguiente caso,
delayPromise
nunca debe ejecutarse simultáneamente, todos deben ejecutarse uno a la vez en un orden de orden de llegada.
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''))
¿Alguna idea sobre cómo configurar una cola como esta?
Tengo una función de "rebote" del maravilloso
Benjamin Gruenbaum
.
Necesito modificar esto para limitar una promesa basada en su propia ejecución y no en el retraso.
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))
}
}
La forma clásica de ejecutar procesos asíncronos en serie es usar
async.js
y
async.series()
.
Si prefiere un código basado en promesas, existe una versión prometedora de
async.js
:
async-q
Con
async-q
puede usar una vez más las
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);
});
Ejecutar dos de ellos al mismo tiempo ejecutará
b
simultáneamente, pero dentro de cada uno serán secuenciales:
// 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''));
Si desea ejecutar
b
después de
a
, póngalo en
.then()
:
async.series(a_array)
.then(()=>{
console.log(''a done'');
return async.series(b_array);
})
.then(()=>{
console.log(''b done'');
});
Si en lugar de ejecutar cada secuencialmente, desea limitar cada uno para ejecutar un número determinado de procesos al mismo tiempo, puede usar
parallelLimit()
:
// Run two promises at a time:
async.parallelLimit(a_array,2)
.then(()=>console.log(''done''));
Lea los documentos async-q: https://github.com/dbushong/async-q/blob/master/READJSME.md
No creo que haya bibliotecas para hacer esto, pero en realidad es bastante simple de implementar:
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;
};
}
Para múltiples solicitudes simultáneas, se vuelve un poco más complicado, pero también se puede hacer.
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)
}
};
}
Por cierto, es posible que desee echar un vistazo al modelo de actores y CSP . Pueden simplificar el manejo de tales cosas, también hay algunas bibliotecas JS para ellos.
Ejemplo
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
Tengo el mismo problema.
Escribí una biblioteca para implementarlo.
El código está
here
Creé una cola para guardar todas las promesas.
Cuando empuja algunas promesas a la cola, las primeras promesas al comienzo de la cola aparecerán y se ejecutarán.
Una vez que se hace una promesa, la siguiente promesa en la cola también se abrirá y se ejecutará.
Una y otra vez, hasta que la cola no tenga
Task
.
Puede consultar el código para más detalles.
Espero que esta biblioteca te ayude.
Utilice el módulo de promesa de aceleración:
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( ... );