На диаграмме жирные линии обозначают время нормальной работы программы, а тонкие – время ожидания I/O. В синхронной модели время, затраченное на I/O, входит во временной график каждого из потоков. В асинхронной, запуск действия по I/O приводит к разветвлению временной линии. Поток, запустивший I/O, продолжает выполнение, а I/O выполняется параллельно ему, по окончанию работы делая обратный вызов функции.
Поток выполнения программы для синхронного и асинхронного I/O.
Ещё один способ выразить эту разницу: в синхронной модели ожидание окончания I/O неявное, а в асинхронной – явное, и находится под нашим непосредственным контролем. Но асинхронность работает в обе стороны. С её помощью выражать программы, не работающие по принципу прямой линии, проще, но выражать прямолинейные программы становится сложнее.
В главе 17 я уже касался того факта, что обратные вызовы привносят кучу шума и делают программу менее упорядоченной. Является ли такой подход в общем хорошей идеей – спорный вопрос. В любом случае, требуется время, чтобы привыкнуть к нему.
Но для системы, основанной на JavaScript, я бы сказал, что использование асинхронности с обратными вызовами имеет смысл. Одна из сильных сторон JavaScript – простота, и попытки добавить в программу несколько потоков привели бы к сильному усложнению. Хотя обратные вызовы не делают код простым, их идея очень проста и в то же время достаточно сильна для того, чтобы писать высокопроизводительные веб-серверы.
Когда в вашей системе установлен Node.js, у вас появляется программа под названием >node
, которая запускает файлы JavaScript. Допустим, у вас есть файл >hello.js
со следующим кодом:
>var message = "Hello world";
>console.log(message);
Вы можете выполнить свою программу из командной строки:
>$ node hello.js
>Hello world
Метод >console.log
в Node действует так же, как в браузере. Выводит кусок текста. Но в Node текст выводится на стандартный вывод, а не в консоль JavaScript в браузере.
Если запустить >node
без файла, он выдаст вам строку запроса, в которой можно писать код на JavaScript и получать результат.
>$ node
>> 1 + 1
>2
>> [-1, -2, -3].map(Math.abs)
>[1, 2, 3]
>> process.exit(0)
>$
Переменная >process
, так же как и >console
, доступна в Node глобально. Она обеспечивает несколько способов для инспектирования и манипулирования программой. Метод >exit
заканчивает процесс, и ему можно передать код статуса окончания программы, который сообщает программе, запустившей >node
(в данном случае, программной оболочке), завершилась ли программа удачно (нулевой код) или с ошибкой (любое другое число).