Comment écrire des fonctions asynchrones pour Node.js

114

J'ai essayé de rechercher comment exactement les fonctions asynchrones devraient être écrites. Après avoir parcouru beaucoup de documents, je ne sais toujours pas.

Comment écrire des fonctions asynchrones pour Node? Comment dois-je implémenter correctement la gestion des événements d'erreur?

Une autre façon de poser ma question serait la suivante: Comment dois-je interpréter la fonction suivante?

var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

Aussi, j'ai trouvé cette question sur SO ("Comment créer une fonction asynchrone non bloquante dans node.js?") Intéressante. Je n'ai pas encore l'impression d'avoir répondu.

Kriem
la source
14
C'est pourquoi je demande. Je ne vois pas en quoi ces fonctions sont différentes.
Kriem
Je vous recommande de regarder setTimeoutet setIntervaldans votre navigateur préféré et de jouer avec eux également. Ou des rappels ajax (probablement la chose la plus proche de l'expérience du nœud), ou des écouteurs d'événements pour des choses que vous connaissez bien, comme les événements de clic et de chargement. Le modèle asynchrone existe déjà dans le navigateur et ils sont exactement les mêmes dans node.
davin
@davin - Je suppose que je ne comprends pas complètement le modèle asynchrone alors.
Kriem
@Kriem, j'ai répondu hier à quelque chose qui pourrait aider: stackoverflow.com/questions/6883648/... Ce n'est pas une réponse à votre question, mais c'est sur le sujet. Essayez de lire la question et d'y répondre et jouez avec le code pour essayer de comprendre ce qui se passe.
davin
2
@Raynos Quelle est la définition de "fonction asynchrone"?
Anderson Green

Réponses:

85

Vous semblez confondre les E / S asynchrones avec les fonctions asynchrones. node.js utilise des E / S non bloquantes asynchrones car les E / S non bloquantes sont meilleures. La meilleure façon de le comprendre est d'aller regarder quelques vidéos de Ryan Dahl.

Comment écrire des fonctions asynchrones pour Node?

Ecrivez simplement des fonctions normales, la seule différence est qu'elles ne sont pas exécutées immédiatement mais transmises en tant que rappels.

Comment dois-je implémenter correctement la gestion des événements d'erreur

Généralement, les API vous donnent un rappel avec une erreur comme premier argument. Par exemple

database.query('something', function(err, result) {
  if (err) handle(err);
  doSomething(result);
});

Est un modèle courant.

Un autre modèle courant est on('error'). Par exemple

process.on('uncaughtException', function (err) {
  console.log('Caught exception: ' + err);
});

Éditer:

var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

La fonction ci-dessus lorsqu'elle est appelée comme

async_function(42, function(val) {
  console.log(val)
});
console.log(43);

Imprime 42sur la console de manière asynchrone. En particulier, se process.nextTickdéclenche après que la pile d'appels eventloop actuelle est vide. Cette pile d'appels est vide après async_functionetconsole.log(43) été exécutée. Nous imprimons donc 43 suivis de 42.

Vous devriez probablement faire un peu de lecture sur la boucle d'événements.

Raynos
la source
J'ai vu les vidéos de Dahl, mais je ne semble pas comprendre le sujet, j'ai peur. :(
Kriem
1
@Kriem voir la réponse mise à jour et en savoir plus sur la boucle de l'événement
Raynos
1
Merci pour les idées. Je suis maintenant plus conscient de ce que je manque de connaissances. :) Votre dernier exemple a aidé par le chemin.
Kriem
Je pense que votre déclaration sur les E / S asynchrones est «meilleure» est trop générale. En ce sens oui, mais globalement ce n'est peut-être pas le cas.
Jake B
Dans votre premier exemple de code, vous vérifiez l'argument err, mais vous n'êtes pas retourné par la suite. En cas d'erreur, le code continuera et causera potentiellement de graves problèmes dans votre application.
Gabriel McAdams
9

Le simple fait de passer par des rappels ne suffit pas. Vous devez utiliser settimer par exemple pour rendre la fonction asynchrone.

Exemples: fonctions non asynchrones:

function a() {
  var a = 0;    
  for(i=0; i<10000000; i++) {
    a++;
  };
  b();
};

function b() {
  var a = 0;    
  for(i=0; i<10000000; i++) {
    a++;
  };    
  c();
};

function c() {
  for(i=0; i<10000000; i++) {
  };
  console.log("async finished!");
};

a();
console.log("This should be good");

Si vous exécutez l'exemple ci-dessus, cela devrait être bon, vous devrez attendre que ces fonctions finissent de fonctionner.

Fonctions pseudo multithread (async):

function a() {
  setTimeout ( function() {
    var a = 0;  
    for(i=0; i<10000000; i++) {
      a++;
    };
    b();
  }, 0);
};

function b() {
  setTimeout ( function() {
    var a = 0;  
    for(i=0; i<10000000; i++) {
      a++;
    };  
    c();
  }, 0);
};

function c() {
  setTimeout ( function() {
    for(i=0; i<10000000; i++) {
    };
    console.log("async finished!");
  }, 0);
};

a();
console.log("This should be good");

Celui-ci sera vraiment asynchrone. Cela devrait être bon sera écrit avant la fin de l'async.

calmbird
la source
3

Si vous SAVEZ qu'une fonction renvoie une promesse, je suggère d'utiliser les nouvelles fonctionnalités async / await de JavaScript. Cela rend la syntaxe synchrone mais fonctionne de manière asynchrone. Lorsque vous ajoutez le asyncmot - clé à une fonction, cela vous permet de faire des awaitpromesses dans cette portée:

async function ace() {
  var r = await new Promise((resolve, reject) => {
    resolve(true)
  });

  console.log(r); // true
}

si une fonction ne renvoie pas de promesse, je recommande de l'envelopper dans une nouvelle promesse que vous définissez, puis de résoudre les données que vous souhaitez:

function ajax_call(url, method) {
  return new Promise((resolve, reject) => {
    fetch(url, { method })
    .then(resp => resp.json())
    .then(json => { resolve(json); })
  });
}

async function your_function() {
  var json = await ajax_call('www.api-example.com/some_data', 'GET');
  console.log(json); // { status: 200, data: ... }
}

En bout de ligne: tirez parti de la puissance des promesses.

ryanwaite28
la source
Il ne faut pas oublier ici que le corps de la promesse est toujours exécuté de manière synchrone.
shadow0359
2

Essayez ceci, cela fonctionne à la fois pour le nœud et le navigateur.

isNode = (typeof exports !== 'undefined') &&
(typeof module !== 'undefined') &&
(typeof module.exports !== 'undefined') &&
(typeof navigator === 'undefined' || typeof navigator.appName === 'undefined') ? true : false,
asyncIt = (isNode ? function (func) {
  process.nextTick(function () {
    func();
  });
} : function (func) {
  setTimeout(func, 5);
});
Pradeep
la source
18
4 votes négatifs et même pas un commentaire constructif ..: \
Omer
6
@Omer Telle est la vie sur SO.
Pièce numérique
6
@NorbertoBezi Peut-être que le code est explicite pour vous, mais pas pour celui qui a publié la réponse. C'est pourquoi c'est toujours une bonne pratique d'expliquer lors d'un vote négatif.
Omer
0

J'ai traité trop d'heures pour une telle tâche pour node.js. Je suis principalement un gars du front-end.

Je trouve cela assez important, car toutes les méthodes de nœuds asynchrones traitent le rappel, et le transformer en Promise est préférable de le gérer.

Je veux juste montrer un résultat possible, plus mince et plus lisible. En utilisant ECMA-6 avec async, vous pouvez l'écrire comme ceci.

 async function getNameFiles (dirname) {
  return new Promise((resolve, reject) => {
    fs.readdir(dirname, (err, filenames) => {
      err !== (undefined || null) ? reject(err) : resolve(filenames)
    })
  })
}

les scénarios (undefined || null)sont pour repl (lecture de la boucle d'impression d'événement), en utilisant également undefined.

Yoarthur
la source