Quelle est la différence entre la programmation synchrone et asynchrone (dans node.js)

192

J'ai lu nodebeginner Et je suis tombé sur les deux morceaux de code suivants.

Le premier:

    var result = database.query("SELECT * FROM hugetable");
    console.log("Hello World");

Le deuxième:

    database.query("SELECT * FROM hugetable", function(rows) {
       var result = rows;
    });
    console.log("Hello World");

J'obtiens ce qu'ils sont censés faire, ils interrogent la base de données pour récupérer la réponse à la requête. Et puis console.log('Hello world').

Le premier est un code supposé synchrone. Et le second est le code asynchrone.

La différence entre les deux pièces est très vague pour moi. Quelle serait la sortie?

Googler sur la programmation asynchrone ne m'a pas non plus aidé.

Azeirah
la source
41
Stange vous n'avez rien trouvé avec google, c'est un sujet assez gros. En programmation synchrone, chaque étape est exécutée une fois que la précédente est terminée. En asynchrone, l'étape 2 sera exécutée même si l'étape 1 n'est pas terminée. La fonction que vous voyez définie dans votre deuxième exemple s'appelle une fonction callBack et sera exécutée dès que le résultat de la base de données sera renvoyé, ce qui sera probablement après l'exécution de console.log.
Laurent S.
7
@Bartdude Il y avait beaucoup de programmation asynchrone, mais aucune explication un peu simple sur ce que c'est, et ce que cela signifie en pratique.
Azeirah
1
@GabrielLlamas Pourquoi éviter les fonctions synchrones?
Charlie Parker
3
@CharlieParker Parce qu'ils bloquent la boucle d'événements et que vous perdez tous les avantages d'un modèle d'E / S à événements asynchrones. Et parce que c'est une mauvaise pratique. Pensez-y de cette façon: si vous n'utilisez pas de fonctions asynchrones, pourquoi utilisez-vous Node.js?
Gabriel Llamas
1
@GabrielLlamas, si j'exécute une requête INSERT et que je veux utiliser le dernier ID inséré après database.query(), je devrais l'appeler de manière synchrone, non? ou quelle devrait être l'approche? (Cette question que j'ai depuis longtemps)
San

Réponses:

230

La différence est que dans le premier exemple , le programme se bloque sur la première ligne. La ligne suivante ( console.log) devra attendre.

Dans le deuxième exemple , le console.logsera exécuté pendant que la requête est en cours de traitement. Autrement dit, la requête sera traitée en arrière-plan, pendant que votre programme fait autre chose, et une fois que les données de la requête sont prêtes, vous en ferez ce que vous voulez.

Donc, en un mot: le premier exemple bloquera, tandis que le second ne le fera pas.

La sortie des deux exemples suivants:

// Example 1 - Synchronous (blocks)
var result = database.query("SELECT * FROM hugetable");
console.log("Query finished");
console.log("Next line");


// Example 2 - Asynchronous (doesn't block) 
database.query("SELECT * FROM hugetable", function(result) {
    console.log("Query finished");
});
console.log("Next line");

Serait:

  1. Query finished
    Next line
  2. Next line
    Query finished

Remarque
Bien que Node lui-même soit à thread unique , certaines tâches peuvent s'exécuter en parallèle. Par exemple, les opérations du système de fichiers se produisent dans un processus différent.

C'est pourquoi Node peut effectuer des opérations asynchrones: un thread effectue des opérations sur le système de fichiers, tandis que le thread principal de Node continue d'exécuter votre code javascript. Dans un serveur événementiel tel que Node, le thread du système de fichiers notifie au thread principal du nœud certains événements tels que l'achèvement, l'échec ou la progression, ainsi que toutes les données associées à cet événement (comme le résultat d'une requête de base de données ou d'une erreur message) et le thread Node principal décide quoi faire avec ces données.

Vous pouvez en savoir plus à ce sujet ici: Fonctionnement du modèle d'E / S non bloquant à thread unique dans Node.js

Salvatorelab
la source
9
Donc, fondamentalement, lorsque j'exécute le premier morceau de code, il fera quelque chose comme ceci request query.; 5 seconds later when the request is done; console.log:; lorsque le second on exécute: request query; console.log; work on the query;
Azeirah
1
@JohnGalt le sql s'exécute sur un thread différent. Mais bien sûr, cela dépend de l'implémentation du pilote SQL que vous utilisez. Le pilote doit générer un nouveau thread, se connecter à mysql et exécuter la requête. Une fois terminé, publiez le résultat dans la file d'attente des événements et Node appellera le rappel.
Salvatorelab
4
N'est-il pas possible pour l'exemple async de générer la même chose que # 1? Comme par exemple, se database.querytermine si vite qu'au moment où nous atteignons console.logla tâche est déjà terminée.
greatwolf
2
@TheBronx si console.log("Next line");dans l'exemple 2 était à l'intérieur de la fonction anonyme, donc juste après console.log("query finished");, cela signifierait que "Next Line" serait imprimé APRÈS "la requête terminée" n'est-ce pas? Donc, si j'ai tout de manière imbriquée, tout fonctionnerait de manière synchrone, donc je n'aurais pas à m'inquiéter d'utiliser des versions synchrones de certaines fonctions. Ai-je raison dans ma compréhension?
Abdul
4
Réponse courte : Oui @Abdul, vous avez raison. Réponse longue : Les fonctions d'imbrication (callbacks) sont le moyen de faire les choses de manière séquentielle, "l'une après l'autre". Mais ce n'est pas techniquement "synchrone". La fonction anonyme est toujours exécutée "lorsque l'opération de blocage est terminée", ou en d'autres termes, "de manière asynchrone". Node.js pourrait exécuter d'autres fonctions pendant cette opération de blocage. Les fonctions restent asynchrones, c'est juste que vous les enchaînez. Les fonctions de synchronisation bloquent l'exécution, c'est la clé.
Salvatorelab
77

La différence entre ces deux approches est la suivante:

De manière synchrone: il attend que chaque opération se termine, après cela seulement il exécute l'opération suivante. Pour votre requête: la console.log()commande ne sera pas exécutée tant que & à moins que la requête ne soit terminée pour obtenir tout le résultat de la base de données.

De manière asynchrone: il n'attend jamais la fin de chaque opération, mais exécute toutes les opérations dans le premier GO uniquement. Le résultat de chaque opération sera traité une fois le résultat disponible. Pour votre requête: La console.log()commande sera exécutée peu de temps après la Database.Query()méthode. Pendant que la requête de base de données s'exécute en arrière-plan et charge le résultat une fois la récupération des données terminée.

Cas d'utilisation

  1. Si vos opérations ne font pas de gros travaux comme l'interrogation d'énormes données à partir de la base de données, continuez avec la manière synchrone sinon la manière asynchrone.

  2. De manière asynchrone, vous pouvez montrer un indicateur de progression à l'utilisateur tandis qu'en arrière-plan, vous pouvez continuer vos travaux lourds. C'est un scénario idéal pour les applications GUI.

Santosh Panda
la source
2
Cela signifie-t-il que db.query (cmd, callback) s'exécute simultanément (comme dans les threads)? Fonctionnent-ils en même temps?
Charlie Parker
Dans son deuxième exemple, y a-t-il une chance que la requête se termine si rapidement qu'elle appelle ensuite le rappel en premier, avant console.log?
Fahmi
@Fahmi théoriquement oui, pratiquement impossible
Leo Messi
24

Cela deviendrait un peu plus clair si vous ajoutez une ligne aux deux exemples:

var result = database.query("SELECT * FROM hugetable");
console.log(result.length);
console.log("Hello World");

Le deuxième:

database.query("SELECT * FROM hugetable", function(rows) {
   var result = rows;
   console.log(result.length);
});
console.log("Hello World");

Essayez de les exécuter et vous remarquerez que le premier exemple (synchrone), result.length, sera imprimé AVANT la ligne «Hello World». Dans le deuxième exemple (asynchrone), le result.length sera (très probablement) imprimé APRÈS la ligne "Hello World".

C'est parce que dans le deuxième exemple, le database.queryest exécuté de manière asynchrone en arrière-plan, et le script continue tout de suite avec le "Hello World". Le console.log(result.length)n'est exécuté que lorsque la requête de base de données est terminée.

Martijn
la source
1
vous dites: le result.length sera (très probablement) imprimé APRÈS la ligne "Hello World". .... pourquoi serait-ce seulement «très probable»? Je pense qu'il est toujours imprimé après la sortie console.log. Merci pour la clarification :)
humanANDpeace
9
@humanityANDpeace: c'est tout l'intérêt de l'accès asynchrone: vous ne savez pas quand cela sera fait. C'est peut-être une base de données absurdement rapide, et la requête de base de données retourne avant même que Javascript n'atteigne la ligne "Hello World" ...
Martijn
19

Premièrement, je me rends compte que je suis en retard pour répondre à cette question.

Avant de parler de synchrone et asynchrone, examinons brièvement le fonctionnement des programmes.

Dans le cas synchrone , chaque instruction se termine avant l'exécution de l'instruction suivante. Dans ce cas, le programme est évalué exactement dans l'ordre des instructions.

C'est ainsi que fonctionne l' asynchrone en JavaScript. Le moteur JavaScript comprend deux parties, une partie qui examine le code et met en file d'attente les opérations et une autre qui traite la file d'attente. Le traitement de la file d'attente se produit dans un thread, c'est pourquoi une seule opération peut se produire à la fois.

Lorsqu'une opération asynchrone (comme la deuxième requête de base de données) est vue, le code est analysé et l'opération est placée dans la file d'attente, mais dans ce cas, un rappel est enregistré pour être exécuté lorsque cette opération se termine. La file d'attente peut déjà contenir de nombreuses opérations. L'opération au début de la file d'attente est traitée et supprimée de la file d'attente. Une fois l'opération pour la requête de base de données traitée, la demande est envoyée à la base de données et une fois terminée, le rappel sera exécuté à la fin. A ce moment, le processeur de file d'attente ayant "traité" l'opération passe à l'opération suivante - dans ce cas

    console.log("Hello World"); 

La requête de base de données est toujours en cours de traitement, mais l'opération console.log est au début de la file d'attente et est traitée. Ceci étant une opération synchrone est exécutée immédiatement résultant immédiatement en la sortie «Hello World». Quelque temps plus tard, l'opération de base de données se termine, alors seulement le rappel enregistré avec la requête est appelé et traité, en définissant la valeur de la variable result en lignes.

Il est possible qu'une opération asynchrone aboutisse à une autre opération asynchrone, cette seconde opération sera mise dans la file d'attente et lorsqu'elle arrivera au début de la file d'attente, elle sera traitée. L'appel du rappel enregistré avec une opération asynchrone permet à l'exécution de JavaScript de renvoyer le résultat de l'opération lorsqu'elle est terminée.

Une méthode simple pour savoir quelle opération JavaScript est asynchrone consiste à noter si elle nécessite un rappel - le rappel est le code qui sera exécuté lorsque la première opération est terminée. Dans les deux exemples de la question, nous pouvons voir que seul le deuxième cas a un rappel, il s'agit donc de l'opération asynchrone des deux. Ce n'est pas toujours le cas en raison des différents styles de gestion du résultat d'une opération asynchrone.

Pour en savoir plus, lisez les promesses. Les promesses sont une autre façon de gérer le résultat d'une opération asynchrone. La bonne chose à propos des promesses est que le style de codage ressemble plus à du code synchrone.

De nombreuses bibliothèques telles que node 'fs' fournissent des styles synchrones et asynchrones pour certaines opérations. Dans les cas où l'opération ne prend pas longtemps et n'est pas beaucoup utilisée - comme dans le cas de la lecture d'un fichier de configuration - l'opération de style synchrone aboutira à un code plus facile à lire.

Geai
la source
6

Dans le cas synchrone, la commande console.log n'est pas exécutée tant que la requête SQL n'est pas terminée.

Dans le cas asynchrone, la commande console.log sera exécutée directement. Le résultat de la requête sera ensuite stocké par la fonction "callback" quelque temps après.

en relation
la source
1
Mais sont-ils réellement appelés simultanément? Ce qui me trouble, c'est que dans le code asynchrone, le code réel est-il exécuté en même temps en parallèle?
Charlie Parker
Cela dépend du processeur (est-il multicœur?) Et du système d'exploitation. Voir en.wikipedia.org/wiki/Multithreading_(software)#Multithreading
relation le
4

La principale différence est qu'avec la programmation asynchrone, vous n'arrêtez pas l'exécution autrement. Vous pouvez continuer à exécuter un autre code pendant que la «demande» est en cours.

thebreiflabb
la source
2

La fonction rend le second asynchrone.

Le premier force le programme à attendre que chaque ligne se termine avant que la suivante puisse continuer. Le second permet à chaque ligne de fonctionner ensemble (et indépendamment) à la fois.

Les langages et les frameworks (js, node.js) qui autorisent l'asynchrone ou la concurrence sont parfaits pour les choses qui nécessitent une transmission en temps réel (par exemple, le chat, les applications stock).

Anton Chan
la source
1

Les fonctions synchrones bloquent tandis que les fonctions asynchrones ne le sont pas. Dans les fonctions synchrones, les instructions se terminent avant l'exécution de l'instruction suivante. Dans ce cas, le programme est évalué exactement dans l'ordre des instructions et l'exécution du programme est interrompue si l'une des instructions prend un temps très long.

Les fonctions asynchrones acceptent généralement un rappel en tant que paramètre et l'exécution se poursuit sur la ligne suivante immédiatement après l'appel de la fonction asynchrone. Le rappel n'est appelé que lorsque l'opération asynchrone est terminée et que la pile d'appels est vide. Les opérations lourdes telles que le chargement de données à partir d'un serveur Web ou l'interrogation d'une base de données doivent être effectuées de manière asynchrone afin que le thread principal puisse continuer à exécuter d'autres opérations au lieu de bloquer jusqu'à la fin de cette longue opération (dans le cas des navigateurs, l'interface utilisateur se fige) .

Orginal Publié sur Github: Lien


la source
1

Programmation asynchrone dans JS:

Synchrone

  • Arrête l'exécution du code supplémentaire jusqu'à ce que cela soit fait.
  • Parce que c'est cet arrêt de l'exécution ultérieure, le code synchrone est appelé «blocage». Blocage dans le sens où aucun autre code ne sera exécuté.

Asynchrone

  • L'exécution de ceci est reportée à la boucle d'événements, c'est une construction dans une machine virtuelle JS qui exécute des fonctions asynchrones (après que la pile de fonctions synchrones est vide).
  • Le code asynchrone est appelé non bloquant car il ne bloque pas l'exécution de code supplémentaire.

Exemple:

// This function is synchronous
function log(arg) {
    console.log(arg)
}

log(1);

// This function is asynchronous
setTimeout(() => {
    console.log(2)
}, 0);

log(3)

  • L'exemple enregistre 1, 3, 2.
  • 2 est enregistré en dernier car il se trouve dans une fonction asynchrone qui est exécutée après que la pile est vide.
Willem van der Veen
la source
0

Programmation de synchronisation

Les langages de programmation comme C, C #, Java sont de la programmation de synchronisation, tout ce que vous écrivez sera exécuté dans l'ordre de votre écriture.

-GET DATA FROM SQL.
//Suppose fetching data take 500 msec

-PERFORM SOME OTHER FUNCTION.
//Performing some function other will take 100 msec, but execution of other 
//task start only when fetching of sql data done (i.e some other function 
//can execute only after first in process job finishes).

-TOTAL TIME OF EXECUTION IS ALWAYS GREATER THAN (500 + 100 + processing time) 
msec

Async

NodeJs propose une fonction asynchrone, il est de nature non bloquante, supposons que dans toute tâche d'E / S qui prend du temps (récupération, écriture, lecture), nodejs ne restera pas inactif et attendra que la tâche soit terminée, c'est ll commence à exécuter les tâches suivantes dans la file d'attente, et chaque fois que cette tâche prend du temps, elle en informe à l'aide de la fonction de rappel. L'exemple suivant aidera:

//Nodejs uses callback pattern to describe functions.
//Please read callback pattern to understand this example

//Suppose following function (I/O involved) took 500 msec
function timeConsumingFunction(params, callback){
  //GET DATA FROM SQL
  getDataFromSql(params, function(error, results){
    if(error){
      callback(error);
    }
    else{
      callback(null, results);
    }
  })
}

//Suppose following function is non-blocking and took 100 msec
function someOtherTask(){
  //some other task
  console.log('Some Task 1');
  console.log('Some Task 2');
}

console.log('Execution Start');

//Start With this function
timeConsumingFunction(params, function(error, results){
    if(error){
      console.log('Error')
    }
    else{
      console.log('Successfull'); 
    }
  })

//As (suppose) timeConsumingFunction took 500 msec, 
//As NodeJs is non-blocking, rather than remain idle for 500 msec, it will start 
//execute following function immediately
someOtherTask();

En bref, la sortie est comme:

Execution Start
//Roughly after 105 msec (5 msec it'll take in processing)
Some Task 1
Some Task 2
//Roughly After 510 msec
Error/Successful //depends on success and failure of DB function execution

La différence est claire là où la synchronisation prendra certainement plus de 600 (500 + 100 + temps de traitement) msec, l'async fait gagner du temps.

Neeraj Bansal
la source