0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Чтение и запись файлов, получение данных по сети, выполнение HTTP-запросов - всё это операции ввода/вывода. Через них программа взаимодействует с внешней средой. Внешняя среда - штука не простая, с большим количеством разнообразных правил, которые необходимо соблюдать. Например, для успешного чтения файла программа должна иметь к нему доступ. Для записи - свободное место на диске. Для выполнения запросов по сети нужно соединение с сетью. Подобных условий десятки, а то и сотни. Невыполнение хотя бы одного из них приводит к ошибке. Посмотрите на этот впечатляющий<a>список из нескольких сотен всевозможных ошибок</a>.</p>
1
<p>Чтение и запись файлов, получение данных по сети, выполнение HTTP-запросов - всё это операции ввода/вывода. Через них программа взаимодействует с внешней средой. Внешняя среда - штука не простая, с большим количеством разнообразных правил, которые необходимо соблюдать. Например, для успешного чтения файла программа должна иметь к нему доступ. Для записи - свободное место на диске. Для выполнения запросов по сети нужно соединение с сетью. Подобных условий десятки, а то и сотни. Невыполнение хотя бы одного из них приводит к ошибке. Посмотрите на этот впечатляющий<a>список из нескольких сотен всевозможных ошибок</a>.</p>
2
<p>В JavaScript обработка ошибок работает через механизм исключений. Одни функции их возбуждают, другие обрабатывают через<em>try..catch</em>. Так было в синхронном коде. В асинхронном стандартный механизм уже не работает.</p>
2
<p>В JavaScript обработка ошибок работает через механизм исключений. Одни функции их возбуждают, другие обрабатывают через<em>try..catch</em>. Так было в синхронном коде. В асинхронном стандартный механизм уже не работает.</p>
3
<p>Подумайте над тем как отработает код ниже:</p>
3
<p>Подумайте над тем как отработает код ниже:</p>
4
<p>Так как<em>try/catch</em>работает только с кодом из текущего стека вызовов, то он не сможет перехватить то, что вызвалось в другом стеке. Поэтому мы не увидим сообщения<em>error!</em>, хотя сама ошибка на экране появится:</p>
4
<p>Так как<em>try/catch</em>работает только с кодом из текущего стека вызовов, то он не сможет перехватить то, что вызвалось в другом стеке. Поэтому мы не увидим сообщения<em>error!</em>, хотя сама ошибка на экране появится:</p>
5
<p>Из вывода видно, что колбек вызвался в своем стеке вызовов, начавшемся внутри функции readFile(). Фактически это означает, что использовать<em>try/catch</em>в асинхронном коде с колбеками - бесполезно, эта конструкция здесь просто неприменима.</p>
5
<p>Из вывода видно, что колбек вызвался в своем стеке вызовов, начавшемся внутри функции readFile(). Фактически это означает, что использовать<em>try/catch</em>в асинхронном коде с колбеками - бесполезно, эта конструкция здесь просто неприменима.</p>
6
<p>Что выведет на экран код ниже?</p>
6
<p>Что выведет на экран код ниже?</p>
7
<p>Правильный ответ:<em>finished!</em>. Это кажется странным, учитывая что ошибка возникла внутри функции readFile(), а не в колбеке. Это происходит потому, что содержимое функции readFile() не принадлежит текущему стеку вызовов.</p>
7
<p>Правильный ответ:<em>finished!</em>. Это кажется странным, учитывая что ошибка возникла внутри функции readFile(), а не в колбеке. Это происходит потому, что содержимое функции readFile() не принадлежит текущему стеку вызовов.</p>
8
<p>Асинхронные функции всегда имеют дело с внешней средой (операционной системой). Это значит, что любая асинхронная функция потенциально может завершиться с ошибкой. Причём не важно возвращает ли она какие-то данные или нет, ошибка может возникнуть всегда. Именно по этой причине колбеки всех асинхронных функций первым параметром принимают ошибку<em>err</em>и, соответственно, проверять её наличие придётся руками. Если пришёл null, то ошибки нет, если не null - есть. Это очень важное<em>соглашение</em>, которого придерживаются не только разработчики стандартной библиотеки, но и все разработчики сторонних решений.</p>
8
<p>Асинхронные функции всегда имеют дело с внешней средой (операционной системой). Это значит, что любая асинхронная функция потенциально может завершиться с ошибкой. Причём не важно возвращает ли она какие-то данные или нет, ошибка может возникнуть всегда. Именно по этой причине колбеки всех асинхронных функций первым параметром принимают ошибку<em>err</em>и, соответственно, проверять её наличие придётся руками. Если пришёл null, то ошибки нет, если не null - есть. Это очень важное<em>соглашение</em>, которого придерживаются не только разработчики стандартной библиотеки, но и все разработчики сторонних решений.</p>
9
<p>В цепочке вызовов придётся делать проверку на каждом уровне:</p>
9
<p>В цепочке вызовов придётся делать проверку на каждом уровне:</p>
10
<p>Тот же самый код, помещенный внутрь функции, выглядит немного по-другому. Как только происходит ошибка, мы вызываем основной колбек и отдаём туда ошибку. Если ошибка не возникла, то мы всё равно вызываем исходный колбек и передаём туда null. Вызывать его обязательно, иначе внешний код не дождётся окончания операции. Следующие вызовы больше не выполняются:</p>
10
<p>Тот же самый код, помещенный внутрь функции, выглядит немного по-другому. Как только происходит ошибка, мы вызываем основной колбек и отдаём туда ошибку. Если ошибка не возникла, то мы всё равно вызываем исходный колбек и передаём туда null. Вызывать его обязательно, иначе внешний код не дождётся окончания операции. Следующие вызовы больше не выполняются:</p>
11
<p>Последний вызов можно сократить. Если в самом конце не было ошибки, то вызов cb(error3) отработает так же, как и cb(null), а значит, весь код последнего колбека можно свести к вызову cb(error3):</p>
11
<p>Последний вызов можно сократить. Если в самом конце не было ошибки, то вызов cb(error3) отработает так же, как и cb(null), а значит, весь код последнего колбека можно свести к вызову cb(error3):</p>
12
12