threads nodejs node nexttick loop guide event avoid node.js promise readfile

node.js - nodejs - Usando promesas con fs.readFile en un bucle



nodejs main loop (3)

Estoy tratando de entender por qué las configuraciones de promesa a continuación no funcionan.

(Nota: ya resolví este problema con async.map. Pero me gustaría saber por qué mis intentos a continuación no funcionaron).

El comportamiento correcto debería ser: bFunc debería ejecutarse tantas veces como sea necesario para que fs lea todos los archivos de imagen (bFunc a continuación se ejecuta dos veces) y luego la consola cFunc imprime "Fin".

¡Gracias!

Intento 1: se ejecuta y se detiene en cFunc ().

var fs = require(''fs''); bFunc(0) .then(function(){ cFunc() }) //cFunc() doesn''t run function bFunc(i){ return new Promise(function(resolve,reject){ var imgPath = __dirname + "/image1" + i + ".png"; fs.readFile(imgPath, function(err, imagebuffer){ if (err) throw err; console.log(i) if (i<1) { i++; return bFunc(i); } else { resolve(); }; }); }) } function cFunc(){ console.log("End"); }

Intento 2: en este caso, utilicé un bucle for, pero se ejecuta fuera de orden. Impresiones de la consola: Fin, bFunc done, bFunc done

var fs = require(''fs''); bFunc() .then(function(){ cFunc() }) function bFunc(){ return new Promise(function(resolve,reject){ function read(filepath) { fs.readFile(filepath, function(err, imagebuffer){ if (err) throw err; console.log("bFunc done") }); } for (var i=0; i<2; i++){ var imgPath = __dirname + "/image1" + i + ".png"; read(imgPath); }; resolve() }); } function cFunc(){ console.log("End"); }

¡Gracias por la ayuda por adelantado!


Nodo v10 tiene un API experimental de promesas

const fsPromises = require(''fs'').promises const func = async filenames => { for(let fn of filenames) { let data = await fsPromises.readFile(fn) } } func([''file1'',''file2''])

https://nodejs.org/api/fs.html#fs_fs_promises_api


Por lo tanto, cada vez que tenga varias operaciones asincrónicas para coordinar de alguna manera, inmediatamente deseo cumplir con las promesas. Y, la mejor manera de usar promesas para coordinar una serie de operaciones asíncronas es hacer que cada operación asíncrona devuelva una promesa. La operación asíncrona de nivel más bajo que se muestra es fs.readFile() . Desde que uso la biblioteca de promesas de Bluebird, tiene una función para "promisificar" el valor de todo un módulo de funciones asíncronas.

var Promise = require(''bluebird''); var fs = Promise.promisifyAll(require(''fs''));

Esto creará nuevos métodos paralelos en el objeto fs con un sufijo "Async" que devuelve promesas en lugar de usar devoluciones de llamada directas. Por lo tanto, habrá un fs.readFileAsync() que devuelve una promesa. Puedes leer más sobre la promisificación de Bluebird here .

Entonces, ahora puede hacer una función que obtenga una imagen de manera bastante simple y que devuelva una promesa cuyo valor sean los datos de la imagen:

function getImage(index) { var imgPath = __dirname + "/image1" + index + ".png"; return fs.readFileAsync(imgPath); }

Luego, en su código, parece que quiere hacer que bFunc() sea ​​una función que lea tres de estas imágenes y llame a cFunc() cuando haya terminado. Puedes hacerlo así:

var Promise = require(''bluebird''); var fs = Promise.promisifyAll(require(''fs'')); function getImage(index) { var imgPath = __dirname + "/image1" + index + ".png"; return fs.readFileAsync(imgPath); } function getAllImages() { var promises = []; // load all images in parallel for (var i = 0; i <= 2; i++) { promises.push(getImage(i)); } // return promise that is resolved when all images are done loading return Promise.all(promises); } getAllImages().then(function(imageArray) { // you have an array of image data in imageArray }, function(err) { // an error occurred });

Si no desea utilizar Bluebird, puede hacer manualmente una versión prometedora de fs.readFile() como esta:

// make promise version of fs.readFile() fs.readFileAsync = function(filename) { return new Promise(function(resolve, reject) { fs.readFile(filename, function(err, data){ if (err) reject(err); else resolve(data); }); }); };

O, en las versiones modernas de node.js, puede usar util.promisify() para hacer una versión promisificada de una función que sigue la convención de llamada asíncrona node.js:

const util = require(''util''); fs.readFileAsync = util.promisify(fs.readFile);

Sin embargo, pronto descubrirá que una vez que comience a usar promesas, querrá usarlas para todas las operaciones asíncronas, por lo que estará "prometiendo" muchas cosas y tendrá una biblioteca o al menos una función genérica que hará eso por usted. Ahorra mucho tiempo.


Tu código debería verse más como esto:

var fs = require(''fs''); var __dirname = "foo"; // promisify fs.readFile() fs.readFileAsync = function (filename) { return new Promise(function (resolve, reject) { try { fs.readFile(filename, function(err, buffer){ if (err) reject(err); else resolve(buffer); }); } catch (err) { reject(err); } }); }; // utility function function getImageAsync(i) { return fs.readFileAsync(__dirname + "/image1" + i + ".png"); }

Uso con una sola imagen:

getImageAsync(0).then(function (imgBuffer){ console.log(imgBuffer); }).catch(function (err) { console.error(err); });

Uso con múltiples imágenes:

var images = [1,2,3,4].map(getImageAsync); Promise.all(images).then(function (imgBuffers) { // all images have loaded }).catch(function (err) { console.error(err); });

Promisificar una función significa tomar una función asíncrona con semántica de devolución de llamada y derivar de ella una nueva función con semántica prometedora.

Se puede hacer manualmente, como se muestra arriba, o, preferiblemente, automáticamente. Entre otros, la biblioteca de promesa de Bluebird tiene un ayudante para eso, consulte here