Почему обратные вызовы JavaScript передаются в качестве аргументов? - PullRequest
0 голосов
/ 18 февраля 2019

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

let result = 0;

function add(num1, num2, callback) {
    setTimeout(() => {
        result = num1 + num2;
        callback();
    }, 2000);
}

function logResult() {
    console.log(result);
}

add(4, 5, logResult); // here's the callback passed as argument

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

let result = 0;

function add(num1, num2) {
    setTimeout(() => {
        result = num1 + num2;
        logResult();
    }, 2000);
}

function logResult() {
    console.log(result);
}

add(4, 5);

Просто для удобства чтения и понимания кода они передаются в качестве аргументов?Или мне чего-то не хватает?Пожалуйста, кто-нибудь может просветить меня?

Ответы [ 5 ]

0 голосов
/ 18 февраля 2019

Прежде всего, слово для мудрых: обратные вызовы считаются плохой практикой.Теперь у нас есть лучшие способы справиться с подобными вещами.В рамках спецификации языка у нас есть Promise s.Что касается внешних библиотек, у нас также есть Observable s.До того как он стал частью спецификации, Promise был построен с использованием обратных вызовов, но предлагал вам более удобочитаемый способ работы с ними, особенно в отношении цепочек обратных вызовов.

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

Например,

function showModal(whichModal) {
  someLibrary.modal(whichModal).show();
  switch (whichModal) {
    case 'createUser':
      someUserLogic();
      break;
    case 'createProject':
      someProjectLogic();
      break;
  }
}

function createUser() {
  showModal('createUser');
}

function createProject() {
  showModal('createProject');
}

против

function showModal(whichModal, postShowCallback) {
  someLibrary.modal(whichModal).show();
  postShowCallback();
}

function createUser() {
  showModal('createUser', someUserLogic);
}

function createProject() {
  showModal('createProject', someProjectLogic);
}

Вы можете увидеть, какбыстро первый пример выйдет из-под контроля, и как элегантно второй решит эту проблему

0 голосов
/ 18 февраля 2019

Обратные вызовы - это механизм для разделения кода.Например, предполагая add часть API с первым кодом, я могу написать:

add(4, 5, console.log);
add(4, 5, alert);
add(4, 5, writeOnTheFileSystemIfNodeJS);
add(4, 5, addToTheDOM);

// etc.

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

Обратите внимание, однако, что в настоящее время для такого рода операций - которые происходят один раз - вывероятно, будет использовать Promises , поскольку они действительно хорошо работают с await / async , где для вещей, которые могут происходить несколько раз, вы, вероятно, захотите использовать events (например,addEventListener) или потоки - в закрытом объекте вы будете использовать асинхронную очередь благодаря асинхронным итераторам и for await.

0 голосов
/ 18 февраля 2019

ему не нужно передавать обратный вызов в качестве аргумента.

Да, он делает ...

setTimeout(() => {

Один обратный вызов,определяется с помощью функции стрелки, передается в setTimeout.

(я знаю, что вы имеете в виду функцию callback, но это все еще обратный вызов и демонстрирует мою точку зрения)


Так какsetTimeout не определенная вами функция, единственный способ определить функцию в области видимости, которую она может охватить, - это сделать ее глобальной.

Затем, если вы хотите, чтобы два экземпляра setTimeout работали нав то же время вы бы назначили первый обратный вызов первому глобальному вызову, а затем второй обратный вызов ... ну ... вы застряли бы.

0 голосов
/ 18 февраля 2019

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

let result = 0;

function add(num1, num2) {
    setTimeout(() => {
        result = num1 + num2;
        logResult();
    }, 2000);
}

function logResult() {
    console.log(result);
}

add(4, 5);

function logResult() {
    console.log(2);
}

В приведенном выше фрагменте функция logResult была поднята , а исходная версия была перезаписана.Обратите внимание, что это произошло, несмотря на тот факт, что add(4, 5) был вызван перед вторым объявлением logResult.

Этой и других проблем с областями видимости можно избежать, приняв вместо этого аргумент обратного вызова.

function add(a, b, callback) {
    setTimeout(() => {
        callback(a + b);
    }, 2000);
}

add(4, 5, (result) => setTimeout(() => console.log(result), 2000));
0 голосов
/ 18 февраля 2019

В вашем примере вам вообще не нужен обратный вызов, вы можете просто сделать:

function add(num1, num2) {
  setTimeout(() => { // thats a callback too, just saying ...
    const result = num1 + num2; // don't leak variables, declare them!
    console.log(result);
  }, 2000);
}

Однако программирование - это создание повторно используемых кодов, которые затем можно составитьв более сложные программы.Поэтому вы не хотите ограничивать использование add для регистрации результата, вместо этого вы можете выполнять различные задачи с ним, если вы принимаете обратный вызов:

 add(1, 2, (result) => {
   add(result, 5, (result2) => {
    alert(result2);
   });
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...