Подтвердить что ты не робот

Зачем использовать обратный вызов в JavaScript, каковы его преимущества?

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

Как правило, я обнаружил, что он используется в AJAX. на httpRequest.onreadystatechange. Это похоже на многопоточность Java? Как и где слушатель ответа? Является ли асинхронное программирование похожим на многопоточность?

В следующем коде, как поток управления:

function some_function(arg1, arg2, callback) {
  var my_number = Math.ceil(Math.random() * (arg1 - arg2) + arg2);
  callback(my_number);
  some_different_function_not_callback(arg1);
}
some_function(5, 15, function(num) {
   console.log("callback called! " + num);
});

На веб-сайте JQuery:

Особенность обратного вызова заключается в том, что функции, которые появляются после "родительского", могут выполняться до выполнения обратного вызова "(ref: http://docs.jquery.com/Tutorials:How_jQuery_Works)

Может ли кто-нибудь объяснить мне эту строку с примером?

4b9b3361

Ответ 1

Основной процесс браузера - это однопоточный цикл событий. Если вы выполняете длительную операцию в однопоточном цикле событий, процесс "блокирует". Это плохо, потому что процесс прекращает обработку других событий в ожидании завершения вашей операции. "alert" - один из немногих способов блокировки браузера: если вы вызываете alert ( "test" ), вы больше не можете нажимать ссылки, выполнять запросы ajax или взаимодействовать с пользовательским интерфейсом браузера.

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

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

Это отличное видео, в котором больше обсуждается цикл событий, используемый в браузере, а также на стороне сервера в node.js.

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

parent_function(function () { console.log('Callback'); });
parent_doesnt_block(); // <-- function appears after "parent"
therefore_execution_continues();
// Maybe we get 'Callback' in the console here? or maybe later...
execution_still_continues();

Ответ 2

Не совсем похоже на многопоточность...

Вы используете обратный вызов в любое время, когда вам нужно ждать чего-то внешнего по отношению к вашему основному JS-коду. В браузере используется тонна для AJAX, а в node.js она используется для каждой отдельной вещи, которая вызывает систему (доступ к файлам, доступ к сети, запросы к базе данных и т.д.).

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

var clicked = function() {
  doAjax('/some/path.json', function(result) {
    updatePageWith(result.widgets);
  });
};

Это запускает код в JS-движке достаточно долго, чтобы выполнить запрос. Затем он простаивает, пока он ждет. Другие JS могут работать на этом этапе, пользовательский интерфейс полностью жидкий и интерактивный, все потрясающе. Затем внезапно все 10 из этих запросов сразу разрешаются. И тогда наш обратный вызов вызывается 10 раз, как магия.

Это работает, потому что каждый раз, когда мы вызываем clicked(), мы создаем новый объект функции и передаем его функции doAjax(). Таким образом, в памяти имеется 10 уникальных функций функции обратного вызова, каждая из которых привязана к определенному запросу с помощью функции doAjax(). Когда запрос возвращается, он находит связанный объект обратного вызова и вызывает его.

Огромное преимущество здесь состоит в том, что, хотя javascript однопоточный, вы никогда не связываете этот поток с ожиданием. Если поток JS занят, это должно произойти только из-за активного запуска кода. Поэтому, хотя JS является однопоточным, тривиально для вашего кода неявно удерживать состояние любого числа любых асинхронных задач.

Синхронный метод обратных вызовов обычно используется для другой цели. Как слушатели или делегаты. Подобно тому, как объект Object A выполняет обратный вызов при изменении данных. Хотя вы строго не асинхронны, вы обычно не вызываете этот обратный вызов немедленно. Вместо этого он будет вызываться позже в ответ на какое-то действие пользователя события.

Ответ 3

Поскольку выполняемый javascript является Asynchronous, поэтому, если вы просто ставите какую-либо старую функцию после выполнения асинхронного запроса, скорее всего, она будет вызвана до завершения первоначального запроса. Первоначальный запрос будет возвращен, как только он BEGINS (отправлен), не будет завершен.

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

Ответ 4

Обратные вызовы используются повсюду в JS, особенно в jQuery.

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

  • Расчет, управляемый событиями, может быть нескоординирован, поэтому обработчики событий являются обратными вызовами:

    <input id="a" /> <button id='add'>+</button> <input id="b" /> = <span id="c"></span>
    <script type="text/javascript">
      $('#add').click(
          function() {
              $('#c').text(parseInt($('#a').val()) + parseInt($('#b').val()));
          }
      );
    </script>
    

    В JS, в частности, координация для отправки событий (например, DOM dispatchEvent, jQuery trigger и IE fireEvent) остается неуказанным (кроме вложенные события DOM, которые являются синхронными). Если вы инициируете событие, обработчики могут быть отложены, и выполнение немедленно возвращается к тому, что есть после триггера. Обработчики событий обычно называются синхронно, но они не обязательно должны быть.

  • JQuery effects обычно выполняет обратный вызов для выполнения после их завершения. Анимационные функции асинхронны, поэтому они не блокируют остальную часть script.

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

  • Вы можете использовать обратные вызовы для фильтрации коллекций:

    // get odd items
    $([0,1,2,3,4,5,6,7,8,9]).filter(function (i) {return this % 2;})
    // or:
    $.grep([0,1,2,3,4,5,6,7,8,9], function (x, i) {return x % 2;});
    
  • Array.sort принимает обратный вызов, чтобы вы могли определить, как сравниваются элементы.

    [{name: 'foo', id: 1}, {name: 'bar', id: 5}, {name: 'baz', id: 3}]
        .sort(function (a,b) { 
            return a.name.localeCompare(b.name); 
        })
    

Некоторые из методов манипуляции JQuery DOM (например, append, prepend и wrap выполнить обратный вызов для создания элемента на основе контекста, предоставляемого методом jQuery. может рассматривать обратный вызов как обеспечивающий внутреннюю часть вычисления или как координацию: при запуске внешнего вычисления необходимые данные для создания новых элементов DOM недоступны, обратный вызов завершает суб-вычисления для создания когда данные становятся доступными.

setTimeout и setInterval оба выполняют обратные вызовы после задержки.

Начиная с версии 1.5, jQuery предлагает отложенные объекты как способ управления несколькими обратными вызовами с различными зависимостями выполнения между обратными вызовами.

Обратный вызов очень похож на "continuation ", что в основном означает "остальная часть вычисления". Разница в том, что продолжение представляет собой всю оставшуюся часть вычисления, тогда как обратный вызов представляет собой остальную часть подсчета. Продолжения являются частью целого стиля программирования, известного как "стиль продолжения прохождения "(CPS). С продолжением вы можете создавать всевозможные интересные структуры управления. Например, продолжения могут использоваться для реализации исключений и coroutines.

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

Ответ 5

Обратные вызовы позволяют однопоточным операциям (Javascript является однопоточным) выполнять асинхронно.

Наиболее очевидным примером являются вызовы AJAX, где у вас есть обратный вызов, который выполняется после выполнения запроса AJAX. Запрос AJAX может занять некоторое время, и если бы это был обычный вызов функции, вся страница была бы заморожена (не может прокручиваться, не может выбирать текст и т.д.), Пока запрос загружается.

Это достигается с помощью setTimeout или setInterval, который вставляет функцию, которая будет вызываться в более позднее время, а также позволяет выполнять другой код между ними. Поэтому, когда вы ожидаете завершения этого вызова AJAX, разрешается выполнение другого кода (включая обновление браузера). Забастовкa >

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

Скажите, что вы хотели анимировать div 100px справа в течение 5 секунд. Возможно, ваш первый инстинкт может создать петлю и поспать между ними. Но в Javascript нет sleep, и даже если бы это было, это просто заморозило бы интерфейс, потому что ничего не может случиться, пока он спит.

Вместо этого вам нужно что-то сделать по строкам, чтобы увеличить положение на 10, затем вызовите setTimeout за 500 мс с обратным вызовом для выполнения следующего кадра. Это, вероятно, будет сделано рекурсивно.

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

Ответ 6

Функция обратного вызова выполняется после завершения текущего эффекта. Более подробный пример доступен здесь.

Ответ 7

Еще один момент - тестируемость кода.

Допустим, у вас есть такой сценарий:

function pushToDatabase(data) {
  // do some API call...
}

function handleData(someData) {
  // some logic

  pushToDatabase(someData);
}

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

function pushToDatabase(data) {
  // do some API call...
}

function handleData(someData, callback) {
  // some logic

  callback(someData);
}

и позвонил с handleData(someData, pushToDatabase).

Это может быть тест с Jest:

const aModule = require('./aModule');

describe('aModule', () => {
  it('should push data to a database', () => {
    const mockFn = jest.fn();
    const myData = 'Hello!';

    aModule.handleData(myData, mockFn)

    expect(mockFn).toHaveBeenCalledWith(myData);
  });
});

Ссылка на рабочую Repl.

Ответ 8

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

function first(){
  //any call or request that is giving us result say abc
  function second(abc){
    //mutate abc, it can be either array, object or can be another function
  }
}

В этих сценариях нам нужно использовать обратный вызов, поскольку первая функция приведет к abc, но никто не знает, сколько времени это действительно займет.

и кроме обратного вызова нужно использовать обещания или async/await, чтобы сделать ваш код более модульным и выглядеть более синхронно

Ответ 9

Является ли асинхронное программирование родственным многопоточности?

да.

Javascript asynch model предоставляет способ выполнения работы "в фоновом режиме".

Предположим, что у вас длинный расчет. Это может быть трудно представить для простой js-демонстрации, но представьте себе длинную процедуру декомпрессии или долговременный алгоритм поиска пути в игре и т.д. Некоторые численные вычисления занимают больше секунды.

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