Utilisation de la chute D'eau asynchrone dans le noeud.js
j'ai 2 fonctions que j'exécute de façon asynchrone. J'aimerais les écrire en utilisant le modèle de chute d'eau. La chose est, je ne sais pas comment..
Voici mon code :
var fs = require('fs');
function updateJson(ticker, value) {
//var stocksJson = JSON.parse(fs.readFileSync("stocktest.json"));
fs.readFile('stocktest.json', function(error, file) {
var stocksJson = JSON.parse(file);
if (stocksJson[ticker]!=null) {
console.log(ticker+" price : " + stocksJson[ticker].price);
console.log("changing the value...")
stocksJson[ticker].price = value;
console.log("Price after the change has been made -- " + stocksJson[ticker].price);
console.log("printing the the Json.stringify")
console.log(JSON.stringify(stocksJson, null, 4));
fs.writeFile('stocktest.json', JSON.stringify(stocksJson, null, 4), function(err) {
if(!err) {
console.log("File successfully written");
}
if (err) {
console.error(err);
}
}); //end of writeFile
} else {
console.log(ticker + " doesn't exist on the json");
}
});
} // end of updateJson
une idée de comment je peux l'écrire en utilisant waterfall, donc je vais être capable de contrôler ça? Écrivez-moi quelques exemples parce que je suis nouveau sur le nœud.js
3 réponses
identifiez D'abord les étapes et écrivez - les comme des fonctions asynchrones (prenant un argument de rappel)
lire le fichier
function readFile(readFileCallback) { fs.readFile('stocktest.json', function (error, file) { if (error) { readFileCallback(error); } else { readFileCallback(null, file); } }); }
traiter le fichier (j'ai supprimé la plupart de la console.journal dans les exemples)
function processFile(file, processFileCallback) { var stocksJson = JSON.parse(file); if (stocksJson[ticker] != null) { stocksJson[ticker].price = value; fs.writeFile('stocktest.json', JSON.stringify(stocksJson, null, 4), function (error) { if (err) { processFileCallback(error); } else { console.log("File successfully written"); processFileCallback(null); } }); } else { console.log(ticker + " doesn't exist on the json"); processFileCallback(null); //callback should always be called once (and only one time) } }
notez que je n'ai pas traité d'erreur spécifique ici, je vais profiter d'async.chute d'eau pour centraliser le traitement des erreurs au même endroit.
faites également attention si vous avez (if/else//interrupteur...) branches dans une fonction asynchrone, il appelle toujours le rappel une (et seulement une) fois.
branchez tout avec async.cascade
async.waterfall([
readFile,
processFile
], function (error) {
if (error) {
//handle readFile error or processFile error here
}
});
Nettoyer exemple
le code précédent était excessivement verbeux pour rendre les explications plus claires. Voici une nettoyés exemple:
async.waterfall([
function readFile(readFileCallback) {
fs.readFile('stocktest.json', readFileCallback);
},
function processFile(file, processFileCallback) {
var stocksJson = JSON.parse(file);
if (stocksJson[ticker] != null) {
stocksJson[ticker].price = value;
fs.writeFile('stocktest.json', JSON.stringify(stocksJson, null, 4), function (error) {
if (!err) {
console.log("File successfully written");
}
processFileCallback(err);
});
}
else {
console.log(ticker + " doesn't exist on the json");
processFileCallback(null);
}
}
], function (error) {
if (error) {
//handle readFile error or processFile error here
}
});
j'ai quitté les noms de fonction parce qu'ils facilitent la lisibilité et le débogage avec des outils comme le débogueur chrome.
Si vous utilisez soulignement (sur npm), vous pouvez également remplacer la première fonction avec _.partial(fs.readFile, 'stocktest.json')
d'Abord et avant tout, assurez-vous d' lire la documentation concernant async.waterfall
.
maintenant, il y a quelques parties clés au sujet du débit de contrôle de la chute d'eau:
- le flux de contrôle est spécifié par un tableau de fonctions pour l'invocation comme premier argument, et un rappel "complet" lorsque le flux est terminé comme second argument.
- le tableau des fonctions est invoqué dans série (par opposition à parallèle.)
- si une erreur (habituellement nommée
err
) est rencontré à n'importe quelle opération dans le tableau de flux, il court-circuit et invoquer immédiatement le "complet"/"fini"/"fait"callback
. - les Arguments de la fonction exécutée précédemment sont appliquer à la fonction suivante dans le flux de contrôle, dans l'ordre, et un rappel "intermédiaire" est fourni comme dernier argument. Note: la première fonction a seulement ce rappel "intermédiaire", et le "complet"" rappel aura les arguments de la dernière fonction invoquée dans le flux de contrôle (en tenant compte de toutes les erreurs), mais avec un
err
argument préprogrammé au lieu d'un rappel" intermédiaire " qui est ajouté. - les callbacks pour chaque opération individuelle (Je l'appelle
cbAsync
dans mes exemples) doit être invoquée lorsque vous êtes prêt à passer: Le premier paramètre est une erreur, le cas échéant, et la deuxième (troisième, quatrième... etc.) paramètre sera toutes les données que vous souhaitez passer à la opération ultérieure.
le premier objectif est de faire fonctionner votre code presque mot à mot avec l'introduction de async.waterfall
. J'ai décidé de supprimer tous vos console.log
déclarations et simplifié votre traitement des erreurs. Voici la première itération (code non testé):
var fs = require('fs'),
async = require('async');
function updateJson(ticker,value) {
async.waterfall([ // the series operation list of `async.waterfall`
// waterfall operation 1, invoke cbAsync when done
function getTicker(cbAsync) {
fs.readFile('stocktest.json',function(err,file) {
if ( err ) {
// if there was an error, let async know and bail
cbAsync(err);
return; // bail
}
var stocksJson = JSON.parse(file);
if ( stocksJson[ticker] === null ) {
// if we don't have the ticker, let "complete" know and bail
cbAsync(new Error('Missing ticker property in JSON.'));
return; // bail
}
stocksJson[ticker] = value;
// err = null (no error), jsonString = JSON.stringify(...)
cbAsync(null,JSON.stringify(stocksJson,null,4));
});
},
function writeTicker(jsonString,cbAsync) {
fs.writeFile('stocktest.json',jsonString,function(err) {
cbAsync(err); // err will be null if the operation was successful
});
}
],function asyncComplete(err) { // the "complete" callback of `async.waterfall`
if ( err ) { // there was an error with either `getTicker` or `writeTicker`
console.warn('Error updating stock ticker JSON.',err);
} else {
console.info('Successfully completed operation.');
}
});
}
la deuxième itération divise un peu plus le flux d'opération. Il le met dans de plus petits morceaux de code à une seule opération. Je ne vais pas le commenter, il parle pour lui-même (encore une fois, non testé):
var fs = require('fs'),
async = require('async');
function updateJson(ticker,value,callback) { // introduced a main callback
var stockTestFile = 'stocktest.json';
async.waterfall([
function getTicker(cbAsync) {
fs.readFile(stockTestFile,function(err,file) {
cbAsync(err,file);
});
},
function parseAndPrepareStockTicker(file,cbAsync) {
var stocksJson = JSON.parse(file);
if ( stocksJson[ticker] === null ) {
cbAsync(new Error('Missing ticker property in JSON.'));
return;
}
stocksJson[ticker] = value;
cbAsync(null,JSON.stringify(stocksJson,null,4));
},
function writeTicker(jsonString,cbAsync) {
fs.writeFile('stocktest.json',jsonString,,function(err) {
cbAsync(err);
});
}
],function asyncComplete(err) {
if ( err ) {
console.warn('Error updating stock ticker JSON.',err);
}
callback(err);
});
}
La dernière itération de court-mains beaucoup de cela avec l'utilisation de certains bind
trucs pour diminuer la pile d'appels et augmenter la lisibilité (IMO), également non testé:
var fs = require('fs'),
async = require('async');
function updateJson(ticker,value,callback) {
var stockTestFile = 'stocktest.json';
async.waterfall([
fs.readFile.bind(fs,stockTestFile),
function parseStockTicker(file,cbAsync) {
var stocksJson = JSON.parse(file);
if ( stocksJson[ticker] === null ) {
cbAsync(new Error('Missing ticker property in JSON.'));
return;
}
cbAsync(null,stocksJson);
},
function prepareStockTicker(stocksJson,cbAsync) {
stocksJson[ticker] = value;
cbAsync(null,JSON.stringify(stocksJson,null,4));
},
fs.writeFile.bind(fs,stockTestFile)
],function asyncComplete(err) {
if ( err ) {
console.warn('Error updating stock ticker JSON.',err);
}
callback(err);
});
}
fondamentalement, les fonctions nodejs (et plus généralement javascript) qui nécessitent un certain temps d'exécution (que ce soit pour le traitement des e/s ou cpu) sont généralement asynchrones, de sorte que la boucle d'événement (pour la rendre simple est une boucle qui vérifie en continu les tâches à exécuter) peut invoquer la fonction juste en dessous de la première, sans se bloquer pour une réponse. Si vous êtes familier avec D'autres langages comme C ou Java, vous pouvez penser à une fonction asynchrone comme une fonction qui tourne sur un autre thread (c'est pas nécessairement vrai dans javascript, mais le programmeur ne devrait pas s'en soucier) et quand l'exécution se termine ce thread avertit le principal (la boucle d'événement un) que le travail est fait et il a les résultats.
comme dit une fois que la première fonction a terminé son travail, elle doit être capable de notifier que son travail est terminé et elle le fait en invoquant la fonction de rappel que vous lui passez. pour faire un exemple:
var callback = function(data,err)
{
if(!err)
{
do something with the received data
}
else
something went wrong
}
asyncFunction1(someparams, callback);
asyncFunction2(someotherparams);
le flux d'exécution appellerait: asyncFunction1, asyncFunction2 et toutes les fonctions ci-dessous jusqu'à ce que asyncFunction1 se termine, puis la fonction de rappel qui est passé comme dernier paramètre à asyncFunction1 est appelé à faire quelque chose avec les données si aucune erreur ne s'est produite.
donc, pour faire exécuter 2 ou plus de fonctions asynchrones une à la suite de l'autre seulement quand elles se terminent, vous devez les appeler à l'intérieur de leurs fonctions de callback:
function asyncTask1(data, function(result1, err)
{
if(!err)
asyncTask2(data, function(result2, err2)
{
if(!err2)
//call maybe a third async function
else
console.log(err2);
});
else
console.log(err);
});
result1 est la valeur de retour d'asyncTask1 et result2 est la valeur de retour d'asyncTask2. Vous peut de cette façon nicher combien de fonctions asynchrones vous voulez.
dans votre cas, si vous voulez qu'une autre fonction soit appelée après updateJson (), vous devez l'appeler après cette ligne:
console.log("File successfully written");