Мне кажется, что функции обратного вызова используются недостаточно, как в JavaScript, при разработке веб-сайтов. Код, использующий обратные вызовы, чаще всего используется в стандартных и сторонних библиотеках, редко используется при разработке веб-приложений. Что очень досадно, поскольку обратные вызовы откровенно классные.

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

Сценарий использования 1: более чистый код

Возьмем эту функцию, которая возвращает сумму двух чисел:

function noCallback(a, b){
   return (a + b);
}
// store the result in a variable and do something with it
let result = noCallback(5,10);
result *= 10;

Теперь давайте посмотрим на тот же код, но с обратным вызовом:

function withCallback(a, b, callback){
    callback(a + b);
}
// do something with the return result without creating a variable
withCallback(5, 10, ( result => result*= 10 ))

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

Пример использования 2: модуляция

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

Давайте взглянем:

// create a paragraph element with some text in it
function createP(text ,callback){
    let paragraph = document.createElement("P");
    paragraph.innerText = text;
    callback(paragraph);
}
// append a paragraph to the body
createP("I am inside the body", p => document.body.appendChild(p))
// append a paragraph to a div
let myDiv = document.getElementById("myDiv");
createP("I am inside a div", p => myDiv.appendChild(p));

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

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

Углубляемся с модуляцией

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

// create a paragraph element with some text in it
function createP(text ,callback){
    let paragraph = document.createElement("P");
    paragraph.innerText = text;
    callback(paragraph);
}
function appendToBody(paragraph){
    document.body.appendChild(paragraph)
}
function appendToDiv(paragraph) {
    document.getElementById("myDiv").appendChild(paragraph);
}
//appending to body
createP("I am inside the body", p => appendToBody(p));
// appending to div
createP("I am inside the div", p => appendToDiv(p));

Как видите, для этого подхода необходимо написать больше кода, но на самом деле вызов этих функций намного проще и менее подвержен ошибкам. Теперь мы также можем тестировать appendToBody и appendToDiv по отдельности.

Заключительные примечания

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