Как и в любом другом языке программирования, в JavaScript есть ряд лучших практик и связанных с ними плохих практик. Из-за своих динамических свойств JavaScript также имеет различные подводные камни.

Как и в любом другом языке программирования, в JavaScript есть ряд лучших практик и связанных с ними плохих практик. Из-за своих динамических свойств JavaScript также имеет различные подводные камни.

Функции обратного вызова — это функции, которые передаются в качестве параметров другим функциям и вызываются ими. Они являются распространенным шаблоном проектирования в асинхронной разработке JavaScript. Базовая (еще не оптимальная) структура этого шаблона проектирования выглядит следующим образом:

Функция doStuff() ожидает функцию в качестве параметра и вызывает ее в определенный момент времени:

Проверьте тип функции обратного вызова

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

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

Если внутри функции есть несколько мест, где может быть вызвана функция обратного вызова, эта проверка должна предшествовать всем этим местам:

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

С помощью условного оператора все это можно даже сократить до одной строки кода:

Параметры функции обратного вызова

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

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

Даже если порядок этих двух параметров в принципе не имеет значения, стало традицией — особенно при разработке модулей Node.js — указывать ошибку как первый параметр, а результат как второй параметр (если ошибки нет, первые параметры, соответствующие нулю).

Вы можете использовать простой запрос if в функции обратного вызова, чтобы узнать, произошла ли ошибка:

Возврат обратного вызова

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

Вот код снова:

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

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

Например, следующий фрагмент показывает неправильный способ сделать это:

Контекст выполнения функции обратного вызова

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

Затем эта новая функция передается как параметр обратного вызова:

Заключение

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

Фундаментальный вопрос о том, следует ли использовать обещания, функции-генераторы или комбинацию async/await для асинхронного программирования вместо функций обратного вызова, будет обсуждаться в следующих статьях.

Это конец объяснения функции обратного вызова в JavaScript. Не стесняйтесь связаться со мной в моем личном информационном бюллетене/блоге, LinkedIn, Twitter и GitHub.

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter и LinkedIn. Посетите наш Community Discord и присоединитесь к нашему Коллективу талантов.