Лично мне не нравятся обратные вызовы, потому что они беспорядочные, нечитаемые и могут (и будут) привести к аду обратных вызовов. Так что моя любовь к обратным вызовам не имеет границ, но иногда людям все же приходится их использовать. В этой статье я постараюсь показать, когда вам нужно использовать обратные вызовы и как вы можете «ограничить» их или даже заменить их чем-то лучшим (да, async / await это так).

Файловые операции

Для выполнения файловых операций вы, вероятно, будете использовать встроенные функции node.js, например fs.readFile:

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

Внешние модули

В node.js довольно часто используются внешние библиотеки (иногда созданные внутри компании), и вы можете быть вынуждены использовать обратные вызовы, потому что эти библиотеки не позволяют async / await. Например, модуль mysql не поддерживает синтаксис async / await, поэтому вам придется использовать обратные вызовы (хорошей альтернативой является mysql2, который имеет поддержку async / await).

Существующая кодовая база

Спорный вопрос, но я постараюсь объяснить, почему может быть причина для использования обратных вызовов. Возможно, вам придется работать с существующими проектами, с полнофункциональным продуктом, который уже запущен в производство (да, иногда мы не запускаем проекты / услуги с нуля, а поддерживаем существующие :)). В данном случае не стоит продвигать философию кода, если она не совпадает с существующей. Представьте, что в проекте нет единственного async / await, а вместо этого везде используются обратные вызовы, а затем вы реализуете некоторые функции или исправляете ошибку с помощью async / await. Если вы это сделаете, то человек, который будет поддерживать этот проект, вероятно, не будет очень благодарен, потому что разработчику нужно будет переключаться между разными стилями реализации в рамках одного и того же сервиса, возможно, даже функцией / классом.

Итак, давайте посмотрим, что мы на самом деле можем сделать с описанными случаями

Просто верните новое обещание

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

Перейдем к другим альтернативам, которые у нас есть.

Используйте внешнюю библиотеку для вашего конкретного случая

Например, для файловых операций вы можете использовать пакет npm await-fs и превратить обратный вызов в обычную функцию async / await. Чтобы использовать его, вам просто нужно потребовать этот модуль вместо модуля fs и использовать его так же, как вы использовали бы fs. Давайте посмотрим код.

Все хорошо, работает нормально и красиво выглядит. Но есть одна маленькая вещь, которая привлекла мое внимание: под капотом эта библиотека просто экспортирует новую функцию, которая возвращает new Promise, обернутую вокруг стандартного обратного вызова node.js.

Возникает вопрос: зачем мне использовать какую-то внешнюю библиотеку, если вы можете легко вернуть new Promise самостоятельно? Что ж, может быть, чтобы сделать вашу кодовую базу немного меньше и чище.

А давайте посмотрим, какой вариант в данном списке последний и чем он лучше описанного.

Используйте Promisify

Promisify - это функция, формирующая встроенную библиотеку node.js util, что означает, что вам не нужно добавлять ее в package.json, а просто требовать. Он помогает преобразовать любую функцию без поддержки async / await в функцию с ней, вам просто нужно придумать имя функции.

И вот наступает дежавю, потому что promisify возвращает new Promise под капотом (проверьте исходный код утилиты).

Заключение

Как мы видим, существует несколько способов минимизировать или избежать обратного вызова и возможности появления ада обратного вызова в проекте node.js. Лично я предпочитаю функцию promisify, но иногда, например, в случае существующей кодовой базы, имеющей собственный стиль, я просто использую new Promise. Не стесняйтесь писать в комментариях, если я пропустили какой-то случай, когда вам нужно использовать обратный вызов или способ (-ы) ограничить или полностью избежать его.