Яка різниця між синхронним та асинхронним програмуванням (у node.js)


189

Я читав nodebeginner І натрапив на наступні два фрагменти коду.

Перший:

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

Другий:

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

Я отримую те, що вони повинні робити, вони запитують базу даних, щоб отримати відповідь на запит. А потім console.log('Hello world').

Перший - це нібито синхронний код. А другий - асинхронний код.

Різниця між двома творами для мене дуже розпливчаста. Яким буде вихід?

Гуглінг по асинхронному програмуванню мені також не допоміг.


41
Шукайте, що ви нічого не знайшли в Google, це досить велика тема. У синхронному програмуванні кожен крок виконується один після завершення виконання попереднього. В асинхронному кроці 2 буде виконано, навіть якщо крок 1 не закінчений. Функція, яку ви бачите, визначена у вашому другому прикладі, називається функцією callBack, і вона буде запущена, як тільки повернеться результат із бази даних, який, ймовірно, буде після запуску console.log.
Лоран С.

7
@Bartdude У асинхронному програмуванні було багато, але дещо простого пояснення того, що це таке, і що це означає на практиці.
Азейра

1
@GabrielLlamas Чому ми повинні уникати синхронних функцій?
Чарлі Паркер

3
@CharlieParker Тому що вони блокують цикл подій, і ви втрачаєте всі переваги від асинхронної моделі з парним введенням-виведенням. А тому, що це погана практика. Подумайте про це так: Якщо ви не використовуєте асинхронні функції, чому ви використовуєте Node.js?
Габріель Лламас

1
@GabrielLlamas, якщо я виконую запит INSERT і хочу використовувати після цього останній вставлений ідентифікатор database.query(), то я повинен викликати його як синхронно, правда? або яким повинен бути підхід? (Це питання у мене давно)
San

Відповіді:


225

Різниця полягає в тому, що в першому прикладі програма буде блокуватися в першому рядку. Наступний рядок ( console.log) доведеться почекати.

У другому прикладі , то console.logбуде виконано, запит обробляється. Тобто запит буде оброблятися у фоновому режимі, тоді як ваша програма займається іншими справами, і як тільки дані запиту будуть готові, ви зробите з ним все, що завгодно.

Отже, у двох словах: перший приклад заблокує, а другий - не.

Вихід з наступних двох прикладів:

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

Було б:

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

Примітка.
Хоча сам Вузол є однопоточним , є деякі завдання, які можуть працювати паралельно. Наприклад, операції з файловою системою відбуваються в іншому процесі.

Ось чому Node може виконувати операції з асинхронізацією: один потік виконує операції з файловою системою, тоді як основний потік Node продовжує виконувати ваш код JavaScript. На сервері, керованому подією, як Node, потік файлової системи повідомляє основний потік Node про певні події, такі як завершення, збій або прогрес, а також будь-які дані, пов’язані з цією подією (наприклад, результат запиту бази даних або помилка повідомлення) і головний потік Node вирішує, що робити з цими даними.

Докладніше про це можна прочитати тут: Як працює модель з однопоточним неблокуючим вводом IO в Node.js


9
Отже, коли я виконую перший фрагмент коду, він зробить щось подібне request query.; 5 seconds later when the request is done; console.log:; коли друга виконує: request query; console.log; work on the query;
Азейра

1
@JohnGalt sql працює на іншому потоці. Але, звичайно, це залежить від реалізації драйвера sql, який ви використовуєте. Драйвер повинен породити новий потік, підключитися до mysql та запустити запит. Після завершення опублікуйте результат у черзі подій , і Node зателефонує до зворотного дзвінка.
Сальваторелаб

4
Чи не можливо для прикладу асинхронізації вивести те саме, що і №1? Як, наприклад, database.queryзакінчується так швидко, що до того часу, коли ми досягнемо console.logзавдання, це вже виконано.
greatwolf

2
@TheBronx, якби console.log("Next line");в прикладі 2 була функція анонімного, то одразу після console.log("query finished");цього це означатиме, що "Наступний рядок" буде надрукований ПІСЛЯ "запит завершений" так? Отже, якщо у мене все є вкладеним способом, все буде працювати синхронно, тому мені не потрібно буде турбуватися про використання синхронних версій певних функцій. Чи правильно я розумію?
Абдул

4
Коротка відповідь : Так @ Abdul, ви праві. Довга відповідь : Функції вставки (зворотні дзвінки) - це спосіб робити речі послідовно, "одна за одною". Але це технічно не "синхронно". Анонімна функція все ще виконується "коли операція блокування закінчена", або іншими словами, "асинхронно". Node.js може виконувати інші функції під час операції блокування. Функції залишаються асинхронними, це лише те, що ви їх пов’язуєте. Функції синхронізації блокують виконання, ось в чому ключ.
Сальваторелаб

75

Різниця між цими двома підходами полягає в наступному:

Синхронний спосіб: він чекає завершення кожної операції, після чого виконує лише наступну операцію. Для вашого запиту: console.log()Команда не буде виконуватися, поки &, якщо запит не буде виконано для отримання всього результату з бази даних.

Асинхронний спосіб: він ніколи не чекає завершення кожної операції, а виконує всі операції лише в першому GO. Результат кожної операції буде оброблятися, коли результат буде доступний. Для вашого запиту: console.log()Команда буде виконана незабаром після Database.Query()методу. Хоча запит до бази даних працює у фоновому режимі і завантажує результат, як тільки він закінчує, отримуючи дані.

Використовуйте випадки

  1. Якщо ваші операції не дуже важкі, наприклад, запит величезних даних з БД, тоді продовжуйте синхронний шлях, інакше асинхронний шлях.

  2. Асинхронним способом ви можете показати деякий показник прогресу користувачеві, тоді як у фоновому режимі ви можете продовжувати ваші великі ваги. Це ідеальний сценарій для програм GUI.


2
Чи означає це, що db.query (cmd, зворотний виклик) працює одночасно (як у потоках)? Вони бігають одночасно?
Чарлі Паркер

У другому прикладі чи є ймовірність, що запит закінчиться так швидко, що потім він спочатку викликає зворотний виклик console.log?
Фахмі

@Fahmi теоретично так, практично зовсім неможливо
Лео Мессі

24

Це стане трохи зрозумілішим, якщо ви додасте рядок до обох прикладів:

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

Другий:

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

Спробуйте запустити їх, і ви помітите, що перший (синхронний) приклад, result.length буде роздрукований перед РОБОМ рядка "Hello World" У другому (асинхронному) прикладі результат.length (швидше за все) буде надрукований ПІСЛЯ рядка "Hello World".

Це тому, що у другому прикладі database.queryфункція запускається асинхронно у фоновому режимі, а сценарій продовжується прямо з "Hello World". Виконується console.log(result.length)лише після завершення запиту до бази даних.


1
Ви кажете: результат.length (швидше за все) буде надрукований ПІСЛЯ рядка "Hello World". .... чому це було б лише "найімовірніше"? Я думаю, що він завжди друкується після виходу console.log. Дякую за роз'яснення :)
humanityANDpeace

9
@humanityANDpeace: у цьому вся суть асинхронного доступу: ви не знаєте, коли це буде зроблено. Можливо, це абсурдно швидка база даних, і запит до бази даних повертається ще до того, як Javascript потрапить до рядка "Hello World" ...
Martijn

19

По-перше, я усвідомлюю, що спізнююсь відповісти на це питання.

Перш ніж обговорити синхронні та асинхронні, давайте коротко розглянемо, як працюють програми.

У синхронному випадку кожне твердження завершується до запуску наступного оператора. У цьому випадку програма оцінюється точно в порядку тверджень.

Так працює асинхронність у JavaScript. У двигуні JavaScript є дві частини, одна частина, яка розглядає код і запитує операції, а інша, яка обробляє чергу. Обробка черги відбувається в одному потоці, тому одночасно може відбуватися лише одна операція.

Коли видно асинхронну операцію (як-от другий запит до бази даних), код аналізується і операція ставиться в чергу, але в цьому випадку реєструється зворотний виклик, який буде запущений після завершення цієї операції. У черзі вже може бути багато операцій. Операція в передній частині черги обробляється і видаляється з черги. Після того, як операція з запитом до бази даних буде оброблена, запит надсилається до бази даних, і коли завершення зворотного дзвінка буде виконано після завершення. В цей час процесор черги, який "обробив" операцію, переходить до наступної операції - у цьому випадку

    console.log("Hello World"); 

Запит на базу даних все ще обробляється, але операція console.log знаходиться на передній частині черги та отримує обробку. Ця синхронна операція виконується негайно, що призводить негайно до виводу "Hello World". Через деякий час операція з базою даних завершується, тільки тоді зворотний виклик, зареєстрований запитом, викликається і обробляється, встановлюючи значення змінної результату на рядки.

Можливо, що одна асинхронна операція призведе до іншої асинхронної операції, ця друга операція буде поставлена ​​у чергу, а коли вона надійде до передньої частини черги, вона буде оброблена. Виклик зворотного дзвінка, зареєстрованого асинхронною операцією, - це те, як час запуску JavaScript повертає результат операції, коли вона виконана.

Простий метод визначити, яка операція JavaScript є асинхронною, - зазначити, якщо вона вимагає зворотного дзвінка - зворотний виклик - це код, який буде виконуватися після завершення першої операції. У двох прикладах у питанні ми бачимо, що лише у другому випадку є зворотний виклик, тож це асинхронна операція обох. Це не завжди так через різні стилі обробки результатів асинхронної операції.

Щоб дізнатися більше, читайте про обіцянки. Обіцяння - це ще один спосіб вирішення результатів асинхронної операції. Приємне в обіцянках полягає в тому, що стиль кодування більше схожий на синхронний код.

Багато бібліотек, як вузол 'fs', надають як синхронний, так і асинхронний стилі для деяких операцій. У випадках, коли операція не займає багато часу і не використовується дуже багато - як у випадку зчитуванням конфігураційного файлу - операція синхронного стилю призведе до коду, який легше читати.


6

У синхронному випадку команда console.log не виконується, поки запит SQL не закінчиться.

У асинхронному випадку команда console.log буде безпосередньо виконана. Результат запиту буде збережено функцією "зворотний виклик" через деякий час.


1
Але чи насправді називають одночасно? Те, що мене бентежить, - це в асинхронному коді, чи фактично паралельно виконується власне код?
Чарлі Паркер

Це залежить від процесора (він багатоядерний?) Та операційної системи. Дивіться en.wikipedia.org/wiki/Multithreading_(software)#Multithreading
пов'язане

4

Основна відмінність полягає в асинхронному програмуванні, ви не зупиняєте виконання інакше. Ви можете продовжувати виконувати інший код під час створення запиту.


2

Функція робить другий асинхронним.

Перший змушує програму чекати, коли кожен рядок закінчить його запуск, перш ніж наступний може продовжуватись. Другий дозволяє кожному рядку працювати разом (і незалежно) відразу.

Мови та фреймворки (js, node.js), які дозволяють асинхронно або одночасно, чудово підходять для речей, які потребують передачі в реальному часі (наприклад, чат, біржові програми).


0

Синхронізація програмування

Мови програмування, такі як C, C #, Java - це програмування синхронізації, що так, коли ви пишете, буде виконуватися в порядку написання.

-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

Асинхронізація

NodeJs придумує функцію асинхронізації, вона не блокує природу, припустимо, у будь-якому завданні вводу / виводу, яке потребує часу (отримання, запис, читання), nodejs не залишаються в режимі очікування і чекають, коли завдання закінчиться, це ' Почніть виконувати наступні завдання в черзі, і кожного разу, коли ця робота буде виконана, вона сповістить, використовуючи зворотний виклик. Наступний приклад допоможе:

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

Якщо коротко, то результат такий:

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

Різниця зрозуміла, коли синхронізація обов'язково займе більше 600 (500 + 100 + час обробки) мсек, асинхронізація економить час.


0

Синхронні функції блокуються, а асинхронні функції - ні. У синхронних функціях оператори завершуються до запуску наступного оператора. У цьому випадку програма оцінюється точно в порядку заяв, а виконання програми призупиняється, якщо одне з тверджень займає дуже тривалий час.

Асинхронні функції зазвичай приймають зворотний виклик як параметр, а виконання продовжується в наступному рядку відразу після виклику асинхронної функції. Зворотний виклик викликається лише тоді, коли асинхронна операція завершена і стек викликів порожній. Важкі операції, такі як завантаження даних з веб-сервера чи запит до бази даних, слід виконувати асинхронно, щоб головний потік міг продовжувати виконувати інші операції замість блокування до завершення цієї тривалої операції (у випадку браузерів інтерфейс буде заморожено) .

Orginal Опубліковано в Github: Посилання


0

Асинхронне програмування в JS:

Синхронний

  • Зупиняє виконання подальшого коду, поки це не буде зроблено.
  • Оскільки це зупинка подальшого виконання, синхронний код називається "блокуючим". Блокування в тому сенсі, що жоден інший код не буде виконаний.

Асинхронний

  • Виконання цього відкладено до циклу подій, це конструкція у віртуальній машині JS, яка виконує асинхронні функції (після того, як стек синхронних функцій порожній).
  • Асинхронний код називається не блокуючим, оскільки він не блокує подальший код від запуску.

Приклад:

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

log(1);

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

log(3)

  • Приклад журналів 1, 3, 2.
  • 2 записується останнім, оскільки він знаходиться всередині асинхронної функції, яка виконується після того, як стек порожній.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.