Всегда ли блокирующий код использует операторы возврата, а неблокирующий код всегда использует обратный вызов?

#node.js #nonblocking

#node.js #неблокирующий

Вопрос:

В руководствах для начинающих обычно демонстрируется неблокирующий характер узла, показывая пример блокировки (с использованием return оператора) и пример неблокирующего узла (с использованием обратных вызовов). Пример смотрите здесь .

Должен ли я воспринимать это как «запах», который с помощью return создает блокирующий код в моем приложении узла, и найти способ повторить его с помощью обратных вызовов?

Ответ №1:

TL; DR: если код может занимать «долгое» время, может быть более чистым / эффективным обрабатывать его с помощью обратного вызова.

Речь идет не о возврате / невозврате, а о том, что на самом деле делает код.

Функция примера не блокируется, потому что есть возврат, она блокируется, потому db.query что занимает произвольное количество времени. Если вы хотите выполнить другие действия в течение этого времени, немедленно вернитесь и выполните обработку результата в обратном вызове.

Следует ли это делать или нет, зависит от того, что происходит на самом деле, на что еще могут повлиять или зависеть данные, переданные в обратный вызов (или возвращенные), и так далее.

Ответ №2:

Блокирующие вызовы обычно возвращают что-то, потому что возврат происходит после «действия», вызванного вызовом, поэтому он может возвращать информацию об успехе или неудаче действия и любые данные, созданные действием. Это относится также к вызовам, которые сами по себе не блокируются, но не выполняют ввод-вывод, поэтому обратный вызов не требуется.

Неблокирующие вызовы, такие как вызов функции, которая считывает файл, как правило, не используют возвращаемое значение, потому что они обычно возвращаются до того, как сделают что-либо значимое. Вместо этого они вызывают обратный вызов и отправляют ему любые данные, которые были созданы, или, возможно, сообщение об ошибке.

Ответ №3:

Основная мотивация использования неблокирующего вызова заключается в том, что вы ожидаете возврата какого-либо внешнего процесса или устройства, поэтому вы освобождаете управление потоком, чтобы позволить другим операциям выполняться «пока вы ждете».

Поскольку неблокирующий / асинхронный код, использующий обратные вызовы, сложнее писать, читать и поддерживать, вам следует избегать неблокирующего кода, за исключением случаев, когда это повлияет на производительность и масштабируемость вашего приложения, или когда это требуется используемым вами API.

Рассмотрим этот относительно глупый пример:

   function add(a,b,cb) { cb(a b); }
  console.log('2 2 is', add(2,2)); // -> 4

  function add(a,b) { return a b; }
  add(2,2,function(sum) { console.log('2 2 is', sum); }); // -> 4
 

Первый вариант немного легче для глаз.

Многие API-интерфейсы узлов имеют асинхронную версию, но не синхронную версию. При использовании этих API будет очевидно, что вам нужно сделать ваш код асинхронным.

В тех случаях, когда у вас есть выбор (например, при чтении файла), спросите себя: «Не заставит ли блокирующий вызов конечного пользователя ждать неоправданно долго?». Во время запуска приложения ответ обычно «нет», потому что этот код выполняется только один раз, и у конечных пользователей не было возможности прикоснуться к серверу. Однако, если вы отвечаете на веб-запрос, то, заставляя всех пользователей ждать исходящего вызова API или чтения с диска, сайт замедлит работу для всех пользователей, тогда как неблокирующий вызов позволяет узлу продолжать обработку новых запросов до тех пор, пока запрошенные вами данные не вернутся.

Комментарии:

1. хотя оба примера неблокирующие (по обычным определениям), поскольку добавление двух чисел не приводит к блокировке работы процессора. обычно это делают операции ввода-вывода.