Utiliser les promesses avec fs.readFile dans une boucle

j'essaie de comprendre pourquoi les arrangements ci-dessous de promesse ne fonctionnent pas.

<!-(Note: j'ai déjà résolu ce problème avec async.cartographie. Mais je voudrais savoir pourquoi mes tentatives ci-dessous ne fonctionne pas.)

le comportement correct devrait être: bFunc devrait exécuter autant de temps que nécessaire pour fs lire tous les fichiers image (bFunc ci-dessous exécute deux fois) et puis cFunc console imprime "End".

Merci!

Tentative 1: Il court et s'arrête à 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");
}

tentative 2: Dans ce cas, j'ai utilisé un for-loop mais il exécute hors de l'ordre. Impressions de la Console: 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");
}

Merci pour l'aide à l'avance!

18
demandé sur David 2016-01-06 11:06:19

3 réponses

donc, à chaque fois que vous avez plusieurs opérations asynchrones à coordonner d'une manière ou d'une autre, je veux immédiatement aller aux promesses. Et, la meilleure façon d'utiliser les promesses de coordonner un certain nombre d'opérations asynchrones est de faire de chaque opération asynchrone retour d'une promesse. Le niveau le plus bas de l'opération asynchrone que vous montrez est fs.readFile(). Depuis que J'utilise la bibliothèque Bluebird promise, elle a une fonction pour "promisifier" toute la valeur d'un module de fonctions asynchrones.

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

cela créera de nouvelles méthodes parallèles sur fs objet avec un suffixe "Async" qui renvoie des promesses au lieu d'utiliser des callbacks simples. Donc, il y aura un fs.readFileAsync() cela rend une promesse. Vous pouvez en savoir plus sur la promisification de L'oiseau bleu ici.

Donc, maintenant vous pouvez faire une fonction qui récupère une image assez simplement et retourne une promesse dont la valeur est les données de l'image:

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

Ensuite, dans ton code, on dirait que vous voulez faire bFunc() une fonction qui lit trois de ces images et ces appels cFunc() quand ils sont faits. Vous pouvez le faire comme ceci:

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 vous ne voulez pas utiliser Bluebird, vous pouvez faire manuellement une version promise de fs.readFile() comme ceci:

// 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);
        });
    });
};

Ou, dans les versions modernes de nœud.js, vous pouvez utiliser util.promisify() pour faire une version promisée d'une fonction qui suit le noeud.js async convention d'appel:

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

cependant, vous trouverez rapidement que lorsque vous commencez à utiliser promesses, vous voulez les utiliser pour toutes les opérations asynchrones donc vous serez "promisifying" beaucoup de choses et d'avoir une bibliothèque ou au moins une fonction générique qui fera cela pour vous permettra de gagner beaucoup de temps.

50
répondu jfriend00 2017-12-24 18:45:14

Votre code devrait ressembler à ceci:

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");
}

Utilisation avec une seule image:

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

Utilisation avec de multiples images:

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

promisify une fonction signifie prendre une fonction asynchrone avec sémantique de rappel et en dériver une nouvelle fonction avec sémantique prometteuse.

Il peut être fait manuellement, comme indiqué ci-dessus, ou – de préférence – automatiquement. Entre autres, la bibliothèque de promesse Bluebird a un assistant pour cela, reportez-vous à http://bluebirdjs.com/docs/api/promisification.html

18
répondu Tomalak 2017-01-26 13:47:29

Le noeud v10 a Expérimentales fs Promesses de l'API

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

3
répondu Dmitry Yudakov 2018-08-28 14:52:47