w3schools son promises los example ejemplos ejemplo cuales comandos javascript promise q sequential serial-processing

son - promise javascript w3schools



Resolver promete uno tras otro(es decir, en secuencia)? (19)

Considere el siguiente código que lee una matriz de archivos de manera serial / secuencial. readFiles devuelve una promesa, que se resuelve solo una vez que todos los archivos se han leído en secuencia.

var Q = require("q"); var readFile = function(file) { ... // Returns a promise. }; var readFiles = function(files) { var deferred = Q.defer(); var readSequential = function(index) { if (index >= files.length) { deferred.resolve(); } else { readFile(files[index]).then(function() { readSequential(index + 1); }); } }; readSequential(0); // Start! return deferred.promise; };

El código del código anterior funciona, pero no me gusta tener que recurrir para que las cosas ocurran de forma secuencial. ¿Hay alguna manera más sencilla de reescribir este código para que no tenga que usar mi extraña función readSequential ?

Originalmente traté de usar Q.all , pero eso causó que todas las llamadas de readFile ocurrieran simultáneamente, que no es lo que quiero:

var readFiles = function(files) { return Q.all(files.map(function(file) { return readFile(file); })); };


Aquí es cómo prefiero ejecutar tareas en serie.

function runSerial() { var that = this; // task1 is a function that returns a promise (and immediately starts executing) // task2 is a function that returns a promise (and immediately starts executing) return Promise.resolve() .then(function() { return that.task1(); }) .then(function() { return that.task2(); }) .then(function() { console.log(" ---- done ----"); }); }

¿Qué pasa con los casos con más tareas? Me gusta, 10?

function runSerial(tasks) { var result = Promise.resolve(); tasks.forEach(task => { result = result.then(() => task()); }); return result; }


Creé este método simple en el objeto Promesa:

Crear y agregar un método Promise.sequence al objeto Promise

Promise.sequence = function (chain) { var results = []; var entries = chain; if (entries.entries) entries = entries.entries(); return new Promise(function (yes, no) { var next = function () { var entry = entries.next(); if(entry.done) yes(results); else { results.push(entry.value[1]().then(next, function() { no(results); } )); } }; next(); }); };

Uso:

var todo = []; todo.push(firstPromise); if (someCriterium) todo.push(optionalPromise); todo.push(lastPromise); // Invoking them Promise.sequence(todo) .then(function(results) {}, function(results) {});

Lo mejor de esta extensión del objeto Promesa es que es consistente con el estilo de las promesas. Promise.all y Promise.sequence se invocan de la misma manera, pero tienen una semántica diferente.

Precaución

La ejecución secuencial de promesas no suele ser una muy buena forma de usar las promesas. Por lo general, es mejor utilizar Promise.all y permitir que el navegador ejecute el código lo más rápido posible. Sin embargo, existen casos de uso real para él, por ejemplo, cuando se escribe una aplicación móvil usando javascript.


Esta es una pequeña variación de otra respuesta anterior. Usando Promesas nativas:

function inSequence(tasks) { return tasks.reduce((p, task) => p.then(task), Promise.resolve()) }

Explicación

Si tiene estas tareas [t1, t2, t3] , entonces lo anterior es equivalente a Promise.resolve().then(t1).then(t2).then(t3) . Es el comportamiento de reducir.

Cómo utilizar

Primero, ¡necesitas construir una lista de tareas! Una tarea es una función que no acepta ningún argumento. Si necesita pasar argumentos a su función, utilice bind u otros métodos para crear una tarea. Por ejemplo:

var tasks = files.map(file => processFile.bind(null, file)) inSequence(tasks).then(...)


Esta pregunta es antigua, pero vivimos en un mundo de ES6 y JavaScript funcional, así que veamos cómo podemos mejorar.

Debido a que las promesas se ejecutan inmediatamente, no podemos simplemente crear una serie de promesas, todas se dispararían en paralelo.

En cambio, necesitamos crear una serie de funciones que devuelva una promesa. Cada función se ejecutará secuencialmente, lo que luego inicia la promesa en el interior.

Podemos resolver esto de varias maneras, pero mi forma favorita es usar reduce .

Se vuelve un poco complicado usar reduce en combinación con las promesas, así que he desglosado el delineador en pequeñas picaduras más pequeñas que se pueden digerir a continuación.

La esencia de esta función es usar reduce comenzando con un valor inicial de Promise.resolve([]) , o una promesa que contiene una matriz vacía.

Esta promesa se pasará al método reduce como una promise . Esta es la clave para encadenar cada promesa de forma secuencial. La próxima promesa de ejecución es func y cuando el fuego then dispara, los resultados se concatenan y esa promesa se devuelve, ejecutando el ciclo de reduce con la siguiente función prometedora.

Una vez que se hayan ejecutado todas las promesas, la promesa devuelta contendrá una matriz de todos los resultados de cada promesa.

Ejemplo ES6 (un trazador de líneas)

/* * serial executes Promises sequentially. * @param {funcs} An array of funcs that return promises. * @example * const urls = [''/url1'', ''/url2'', ''/url3''] * serial(urls.map(url => () => $.ajax(url))) * .then(console.log.bind(console)) */ const serial = funcs => funcs.reduce((promise, func) => promise.then(result => func().then(Array.prototype.concat.bind(result))), Promise.resolve([]))

Ejemplo ES6 (desglosado)

// broken down to for easier understanding const concat = list => Array.prototype.concat.bind(list) const promiseConcat = f => x => f().then(concat(x)) const promiseReduce = (acc, x) => acc.then(promiseConcat(x)) /* * serial executes Promises sequentially. * @param {funcs} An array of funcs that return promises. * @example * const urls = [''/url1'', ''/url2'', ''/url3''] * serial(urls.map(url => () => $.ajax(url))) * .then(console.log.bind(console)) */ const serial = funcs => funcs.reduce(promiseReduce, Promise.resolve([]))

Uso:

// first take your work const urls = [''/url1'', ''/url2'', ''/url3'', ''/url4''] // next convert each item to a function that returns a promise const funcs = urls.map(url => () => $.ajax(url)) // execute them serially serial(funcs) .then(console.log.bind(console))


Esto se extiende a cómo procesar una secuencia de promesas de una manera más genérica, soportando secuencias dinámicas / infinitas, basadas en la implementación de spex.sequence :

var $q = require("q"); var spex = require(''spex'')($q); var files = []; // any dynamic source of files; var readFile = function (file) { // returns a promise; }; function source(index) { if (index < files.length) { return readFile(files[index]); } } function dest(index, data) { // data = resolved data from readFile; } spex.sequence(source, dest) .then(function (data) { // finished the sequence; }) .catch(function (error) { // error; });

No solo esta solución funcionará con secuencias de cualquier tamaño, sino que también puede agregarle fácilmente regulación de velocidad y equilibrio de carga .


La mejor solución que pude descifrar fue con las promesas de los bluebird . Puede hacer Promise.resolve(files).each(fs.readFileAsync); que garantiza que las promesas se resuelven secuencialmente en orden.


Mi respuesta está basada en https://.com/a/31070150/7542429 .

Promise.series = function series(arrayOfPromises) { var results = []; return arrayOfPromises.reduce(function(seriesPromise, promise) { return seriesPromise.then(function() { return promise .then(function(result) { results.push(result); }); }); }, Promise.resolve()) .then(function() { return results; }); };

Esta solución devuelve los resultados como una matriz como Promise.all ().

Uso:

Promise.series([array of promises]) .then(function(results) { // do stuff with results here });


Mi solución preferida:

function processArray(arr, fn) { return arr.reduce( (p, v) => p.then((a) => fn(v).then(r => a.concat([r]))), Promise.resolve([]) ); }

No es fundamentalmente diferente de otros publicados aquí, pero:

  • Aplica la función a elementos en serie
  • Resuelve una variedad de resultados
  • No requiere async / await (el soporte es bastante limitado, alrededor de 2017)
  • Utiliza funciones de flecha; agradable y conciso

Ejemplo de uso:

const numbers = [0, 4, 20, 100]; const multiplyBy3 = (x) => new Promise(res => res(x * 3)); // Prints [ 0, 12, 60, 300 ] processArray(numbers, multiplyBy3).then(console.log);

Probado en corriente razonable Chrome (v59) y NodeJS (v8.1.2).


Para hacer esto simplemente en ES6:

function(files) { // Create a new empty promise (don''t do that with real people ;) var sequence = Promise.resolve(); // Loop over each file, and add on a promise to the // end of the ''sequence'' promise. files.forEach(function(file) { // Chain one computation onto the sequence sequence = sequence.then(function() { return performComputation(file); }).then(function(result) { doSomething(result) // Resolves for each file, one at a time. }); }) // This will resolve after the entire chain is resolved return sequence; }


Puede usar esta función que obtiene la lista de promiseFactories:

function executeSequentially(promiseFactories) { var result = Promise.resolve(); promiseFactories.forEach(function (promiseFactory) { result = result.then(promiseFactory); }); return result; }

Promise Factory es una función simple que devuelve una Promesa:

function myPromiseFactory() { return somethingThatCreatesAPromise(); }

Funciona porque una fábrica de promesas no crea la promesa hasta que se le solicite. Funciona de la misma manera que una función en ese momento; de hecho, ¡es lo mismo!

No quiere operar sobre una serie de promesas. Según la especificación Promesa, tan pronto como se crea una promesa, comienza a ejecutarse. Entonces, lo que realmente quieres es una serie de fábricas prometedoras ...

Si desea obtener más información sobre Promises, debe consultar este enlace: pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html


Realmente me gustó la respuesta de @ joelnet, pero para mí, ese estilo de codificación es un poco difícil de digerir, así que pasé un par de días tratando de descubrir cómo expresaría la misma solución de una manera más legible y este es mi tomar, solo con una sintaxis diferente y algunos comentarios.

// first take your work const urls = [''/url1'', ''/url2'', ''/url3'', ''/url4''] // next convert each item to a function that returns a promise const functions = urls.map((url) => { // For every url we return a new function return () => { return new Promise((resolve) => { // random wait in milliseconds const randomWait = parseInt((Math.random() * 1000),10) console.log(''waiting to resolve in ms'', randomWait) setTimeout(()=>resolve({randomWait, url}),randomWait) }) } }) const promiseReduce = (acc, next) => { // we wait for the accumulator to resolve it''s promise return acc.then((accResult) => { // and then we return a new promise that will become // the new value for the accumulator return next().then((nextResult) => { // that eventually will resolve to a new array containing // the value of the two promises return accResult.concat(nextResult) }) }) }; // the accumulator will always be a promise that resolves to an array const accumulator = Promise.resolve([]) // we call reduce with the reduce function and the accumulator initial value functions.reduce(promiseReduce, accumulator) .then((result) => { // let''s display the final value here console.log(''=== The final result ==='') console.log(result) })


Si lo desea, puede usar reducir para hacer una promesa secuencial, por ejemplo:

[2,3,4,5,6,7,8,9].reduce((promises, page) => { return promises.then((page) => { console.log(page); return Promise.resolve(page+1); }); }, Promise.resolve(1));

siempre funcionará en secuencia.


Sobre la base del título de la pregunta, "Resolver promete una tras otra (es decir, en secuencia)?", Podríamos entender que la OP está más interesada en el manejo secuencial de las promesas de liquidación que las llamadas secuenciales per se .

Esta respuesta se ofrece:

  • para demostrar que las llamadas secuenciales no son necesarias para el manejo secuencial de las respuestas.
  • para exponer patrones alternativos viables a los visitantes de esta página, incluido el OP si todavía está interesado más de un año después.
  • a pesar de la afirmación de OP de que no desea hacer llamadas concurrentemente, lo que puede ser genuino, pero igualmente puede ser una suposición basada en el deseo de manejo secuencial de las respuestas como lo implica el título.

Si las llamadas simultáneas no son realmente deseadas, entonces vea la respuesta de Benjamin Gruenbaum que cubre llamadas secuenciales (etc.) de manera exhaustiva.

Sin embargo, si está interesado (para un mejor rendimiento) en patrones que permiten llamadas concurrentes seguidas de un manejo secuencial de las respuestas, continúe leyendo.

Es tentador pensar que tienes que usar Promise.all(arr.map(fn)).then(fn) (como lo he hecho muchas veces) o un azúcar de fantasía de Promise lib (notablemente Bluebird), sin embargo (con crédito para este artículo) ) un arr.map(fn).reduce(fn) hará el trabajo, con las ventajas de que:

  • funciona con cualquier promesa lib - incluso versiones .then() de jQuery - solo se usa .then() .
  • ofrece la flexibilidad de saltar por error o detener-en-error, lo que quieras con un mod de una línea.

Aquí está, escrito para Q

var readFiles = function(files) { return files.map(readFile) //Make calls in parallel. .reduce(function(sequence, filePromise) { return sequence.then(function() { return filePromise; }).then(function(file) { //Do stuff with file ... in the correct sequence! }, function(error) { console.log(error); //optional return sequence;//skip-over-error. To stop-on-error, `return error` (jQuery), or `throw error` (Promises/A+). }); }, Q()).then(function() { // all done. }); };

Nota: solo ese fragmento, Q() , es específico de Q. Para jQuery debe asegurarse de que readFile () devuelva una promesa jQuery. Con A + libs, las promesas extranjeras serán asimiladas.

La clave aquí es la promesa de sequence de reducción, que secuencia el manejo de las promesas de readFile pero no su creación.

Y una vez que hayas absorbido eso, tal vez sea un poco alucinante cuando te .map() cuenta de que el escenario .map() no es realmente necesario. Todo el trabajo, las llamadas en paralelo y el manejo en serie en el orden correcto, se puede lograr con reduce() solo, además de la ventaja adicional de mayor flexibilidad para:

  • Convierta de llamadas asincrónicas paralelas a llamadas asincrónicas en serie simplemente moviendo una línea, potencialmente útil durante el desarrollo.

Aquí está, para Q nuevamente.

var readFiles = function(files) { return files.reduce(function(sequence, f) { var filePromise = readFile(f);//Make calls in parallel. To call sequentially, move this line down one. return sequence.then(function() { return filePromise; }).then(function(file) { //Do stuff with file ... in the correct sequence! }, function(error) { console.log(error); //optional return sequence;//Skip over any errors. To stop-on-error, `return error` (jQuery), or `throw error` (Promises/A+). }); }, Q()).then(function() { // all done. }); };

Ese es el patrón básico. Si también desea entregar datos (por ejemplo, los archivos o alguna transformación de ellos) a la persona que llama, necesitaría una variante leve.


Su enfoque no es malo, pero tiene dos problemas: se traga errores y emplea el Antipatín de construcción de promesas explícitas.

Puede resolver estos dos problemas y hacer que el código sea más limpio, al tiempo que sigue empleando la misma estrategia general:

var Q = require("q"); var readFile = function(file) { ... // Returns a promise. }; var readFiles = function(files) { var readSequential = function(index) { if (index < files.length) { return readFile(files[index]).then(function() { return readSequential(index + 1); }); } }; // using Promise.resolve() here in case files.length is 0 return Promise.resolve(readSequential(0)); // Start! };


Tuve que ejecutar muchas tareas secuenciales y usé estas respuestas para forjar una función que se encargaría de manejar cualquier tarea secuencial ...

function one_by_one(objects_array, iterator, callback) { var start_promise = objects_array.reduce(function (prom, object) { return prom.then(function () { return iterator(object); }); }, Promise.resolve()); // initial if(callback){ start_promise.then(callback); }else{ return start_promise; } }

La función toma 2 argumentos + 1 opcional. El primer argumento es la matriz en la que trabajaremos. El segundo argumento es la tarea en sí, una función que devuelve una promesa, la siguiente tarea se iniciará solo cuando se resuelva esta promesa. El tercer argumento es una devolución de llamada para ejecutar cuando se hayan realizado todas las tareas. Si no se pasa una devolución de llamada, la función devuelve la promesa que creó para que podamos manejar el final.

Aquí hay un ejemplo de uso:

var filenames = [''1.jpg'',''2.jpg'',''3.jpg'']; var resize_task = function(filename){ //return promise of async resizing with filename }; one_by_one(filenames,resize_task );

Espero que le ahorre a alguien algo de tiempo ...


Uso el siguiente código para extender el objeto Promesa. Maneja el rechazo de las promesas y devuelve una variedad de resultados

Código

/* Runs tasks in sequence and resolves a promise upon finish tasks: an array of functions that return a promise upon call. parameters: an array of arrays corresponding to the parameters to be passed on each function call. context: Object to use as context to call each function. (The ''this'' keyword that may be used inside the function definition) */ Promise.sequence = function(tasks, parameters = [], context = null) { return new Promise((resolve, reject)=>{ var nextTask = tasks.splice(0,1)[0].apply(context, parameters[0]); //Dequeue and call the first task var output = new Array(tasks.length + 1); var errorFlag = false; tasks.forEach((task, index) => { nextTask = nextTask.then(r => { output[index] = r; return task.apply(context, parameters[index+1]); }, e=>{ output[index] = e; errorFlag = true; return task.apply(context, parameters[index+1]); }); }); // Last task nextTask.then(r=>{ output[output.length - 1] = r; if (errorFlag) reject(output); else resolve(output); }) .catch(e=>{ output[output.length - 1] = e; reject(output); }); }); };

Ejemplo

function functionThatReturnsAPromise(n) { return new Promise((resolve, reject)=>{ //Emulating real life delays, like a web request setTimeout(()=>{ resolve(n); }, 1000); }); } var arrayOfArguments = [[''a''],[''b''],[''c''],[''d'']]; var arrayOfFunctions = (new Array(4)).fill(functionThatReturnsAPromise); Promise.sequence(arrayOfFunctions, arrayOfArguments) .then(console.log) .catch(console.error);


Utilice Array.prototype.reduce , y recuerde cumplir sus promesas en una función; de lo contrario, ya se estarán ejecutando.

// array of Promise providers const providers = [ function(){ return Promise.resolve(1); }, function(){ return Promise.resolve(2); }, function(){ return Promise.resolve(3); } ] const seed = Promise.resolve(null); const inSeries = function(providers){ return providers.reduce(function(a,b){ return a.then(b); }, seed); };

agradable y fácil ... deberías poder volver a utilizar la misma semilla para el rendimiento, etc.


Utilización simple para la promesa estándar de Node.js:

function sequence(tasks, fn) { return tasks.reduce((promise, task) => promise.then(() => fn(task)), Promise.resolve()); }

ACTUALIZAR

items-promise es un paquete de NPM listo para usar que hace lo mismo.


Actualización 2017 : Utilizaría una función asíncrona si el entorno lo admite:

async function readFiles(files) { for(const file of files) { await readFile(file); } };

Si lo desea, puede diferir la lectura de los archivos hasta que los necesite utilizando un generador asíncrono (si su entorno lo admite):

async function* readFiles(files) { for(const file of files) { yield await readFile(file); } };

Actualización: Pensándolo bien: podría usar un bucle for en su lugar:

var readFiles = function(files) { var p = Q(); // Promise.resolve() without Q files.forEach(function(file){ p = p.then(function(){ return readFile(file); }); // or .bind }); return p; };

O más compacto, con reducir:

var readFiles = function(files) { return files.reduce(function(p, file) { return p.then(function(){ return readFile(file); }); },Q()); // initial };

En otras bibliotecas de promesas (como cuándo y Bluebird) tienes métodos de utilidad para esto.

Por ejemplo, Bluebird sería:

var Promise = require("bluebird"); var fs = Promise.promisifyAll(require("fs")); var readAll = Promise.resolve(files).map(fs.readFileAsync,{concurrency: 1 }); // if the order matters, you can use Promise.each instead and omit concurrency param readAll.then(function(allFileContents){ // do stuff to read files. });

En Q, lo que tienes es lo mejor que puedes obtener: puedes acortarlo con Array.prototype.reduce un poco y extraerlo en un método genérico.

Si puede usar Q.async (es decir, está en el nodo) las cosas mejoran:

Q.spawn(function* () { var results = []; for(var i = 0;i < files.length; i++){ results.push(yield readFile(files[i])); } console.log(results); });

Solo recuerde ejecutar el nodo con --harmony y recuerde que es atm experimental .