Что такое отложенные обратные вызовы?


15

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

Я изо всех сил пытаюсь понять отложенные обратные вызовы, даже после поиска в Google.

Может ли кто-нибудь дать простое объяснение, пожалуйста? Я программирую на Ruby, но также немного знаю C / C ++, но больше всего я был опытным программистом на ассемблере. Итак, мне интересно, это немного похоже на стек обратных вызовов, которые получают pop'd? Я надеюсь изучить jquery или node.js, и эти отложенные обратные вызовы кажутся неотъемлемой частью обоих. Я понимаю основные принципы работы с потоками (хотя объект-мьютекс заставляет мою голову болеть;)


Вы имеете в виду Deferredобъекты jQuery ? Это что-то конкретное для Node.js?
bfavaretto

1
Нет, я имею в виду в целом. Хотя я действительно хочу изучать jquery и, возможно, node.js, я чувствовал, что мне нужно понять, что на самом деле является отложенным обратным вызовом. Я прочитал статью в Википедии о обратных вызовах, но не смог понять отложенных обратных вызовов, которые кажутся неотъемлемыми для парадигмы асинхронной операции, которая будет задействована в этих языках, которые ее используют.


1
Я действительно прошу концептуальную идею отложенного обратного вызова в отличие от их реализации - извините, если я не прояснил это. Я привел дополнительные языковые примеры, чтобы объяснить идею, которую я пытаюсь прояснить, а также мои знания в области программирования, чтобы люди знали, как дать ответ. Большое спасибо за ответы до сих пор - я получаю там!
десять раз

Хорошо, я думаю, что теперь у меня это есть, спасибо вам всем! Я не знаю, каким образом сделать ответ, хотя. Кэмерон объяснила концепцию наиболее просто, и это было то, к чему я действительно стремилась, но другие также присоединились к моим знаниям. Я не уверен, какой способ принять ответ, поскольку я новичок в этом;)
десять раз

Ответы:


4

По желанию, вот комментарии, представленные в качестве ответа:


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

Например, скажем, вы хотите записать в файл, а затем распечатать сообщение журнала; поэтому вы вызываете функцию «write ()» (или как угодно) и передаете ей функцию, которая выводит сообщение журнала (это функция отложенного обратного вызова). «write ()» внутренне хранит ссылку на данную функцию, начинает запись в файл и устанавливает свой собственный обратный вызов, чтобы знать, когда запись завершена. Затем он возвращается до завершения записи; когда это так, внутренний обратный вызов как-то вызывается (это работа базового фреймворка - в случае с node.js это делается с помощью цикла обработки событий), который затем вызывает ваш обратный вызов, который печатает сообщение журнала.

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

Большая часть содержимого является async в node.js, но в браузере с, например, jQuery, большая часть содержимого фактически синхронна (за исключением, очевидно, для запросов AJAX). Так как первоклассные функции очень удобны в JavaScript (особенно из-за большой поддержки замыканий), обратные вызовы также используются повсюду в браузере, но они не «откладываются» для синхронных операций (кроме тех случаев, когда они не вызываются немедленно вы, но позже с помощью функции, которую вы вызываете).

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


8

Способ отложенного обратного вызова работает каждый раз, когда вы добавляете к нему обратный вызов, этот обратный вызов помещается в массив. Затем, когда метод .resolve()or .resolveWith()вызывается для отложенного объекта, все .done()обратные вызовы в массиве выполняются по порядку.

Теперь мы можем посмотреть, что такое отложенный объект. Возьмите фрагмент ниже в качестве примера.

var deferred = $.Deferred();
var promise = deferred.promise();

Теперь у нас есть отложенный объект и объект обещания отложенного объекта. Отложенный объект имеет все те же методы, что и объект обещает, однако объект обещания имеет только методы .done(), .fail()и .always()которые используются для добавления обратных вызовов к отсроченному объекту для каждого соответствующего event. У отложенного объекта, с другой стороны, есть несколько других методов, наиболее важно .resolve()и .reject(). Когда эти методы вызываются для отложенного объекта, все обратные вызовы вызываются. .resolve()запускает .done()и .always()обратные вызовы, а .reject()метод вызывает .fail()и .always()обратные вызовы.

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

Вот еще один пример, который я использую:

function loadImage(url) {
    var def = $.Deferred(),
        img = new Image();
    $(img).on("load error",function(e){
        if (e.type === "error") {
            def.reject(url);
        }
        else {
            def.resolve(url);
        }
    });
    img.src = url;
    // return the promise object so that callbacks can
    // be defined on the deferred object.
    return def.promise();
}
loadImage("foobar.jpg").done(function(){
    alert("The image is loaded!");
}).fail(function(){
    alert("The image failed to load!");
}).always(function(){
    alert("This is always called!");
});

Для получения дополнительной информации о $.Deferred()методе jQuery и отложенных объектах, посетите http://api.jquery.com/category/deferred-object/


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

3

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

Лучшее объяснение, которое я нашел, было на http://www.nodebeginner.org

Эй, возможно ExextFunction (), пожалуйста, сделайте свое дело, но я, единственный поток Node.js, не собираюсь ждать здесь до тех пор, пока вы не закончите, я продолжу выполнять строки кода под вами, так что вы, пожалуйста, возьмите this callbackFunction () здесь и вызывать его, когда вы закончите делать свои дорогие вещи? Благодарность!"

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

В качестве примера, вероятно, дорогой функции вы можете взять fs.readFile


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

Отредактировал ответ, может быть, теперь он понятнее.

Очень полезно. Но ... я должен обработать этот массив сохраненных обратных вызовов? Я имею в виду, что именно обрабатывает этот список. Является ли (например), что js поднимает эти обратные вызовы в фоновом режиме, когда вам ничего не нужно делать, или это происходит из-за того, что событие вызывает node.js или что-то, что вызывает определенный обратный вызов. Извините, я получаю около 70% от того, что вы говорите, но я немного растерялся от остального :)
десять раз

@tentimes - «что обрабатывает этот список» объект $ .Deferred () обрабатывает список. Когда вы вызываете .resolve()или .reject()исходный отложенный объект, вызывается список обратных вызовов.
user400654

2
@tentimes: Исходя из того, что вы говорите, я не уверен, что вы полностью поняли тот факт, что функции в JS являются первоклассными объектами и, следовательно, могут храниться до тех пор, пока они не понадобятся, после того времени, когда они были созданы. Например, вы хотите записать в файл, а затем распечатать сообщение журнала; поэтому вы вызываете функцию «write» (или как угодно) и передаете ей функцию, которая выводит сообщение журнала. «write ()» внутренне хранит ссылку на данную функцию, начинает запись в файл и устанавливает свой собственный обратный вызов, чтобы знать, когда запись завершена. Затем он возвращается до завершения записи; когда это так, ваша функция вызывается.
Кэмерон

3

JavaScript является однопоточным, поэтому вы не можете думать с точки зрения потоков, чтобы понять это. Вот пример как обычных, так и асинхронных обратных вызовов с использованием jQuery:

var regularCallback = function(evt) {
    alert("I'm a callback!")
}
var asyncCallback = function(data) {
    alert("I only run when an async operation finishes!")
}

// Bind the regular callback to a button's click event
$('#mybutton').on('click', regularCallback);

// Start an ajax request to the server. The request is asynchronous, so code
// below this line will execute immediately. The callback function
// will only be called when the request is complete.
$.get("http://google.com", asyncCallback);

Теперь это куда-то меня ведет, спасибо. Таким образом, асинхронный ответ на событие, которое я настроил с помощью ajax, - который просто обрабатывает его, и если событие происходит, вызывается мой обратный вызов? Я думаю, что я получаю это. Будет ли node.js / jquery делать что-то подобное с отложенными объектами jquery usnig и сложной системой взаимодействия с ними, чтобы действительно делать то же самое, но с использованием методов?
десять раз

1
@ время, точно! Обратные вызовы обычно выполняются в ответ на события (но поскольку «обратный вызов» не является языковой конструкцией js, иногда этот термин используется в других контекстах). Отложенные объекты (обещания), которые вы видите в ответе Кевина, в основном являются синтаксическим сахаром. Они «разрешаются» или «отклоняются», когда запускается какое-либо (асинхронное) событие, затем они вызывают соответствующий обратный вызов («выполнено» или «не выполнено», затем «всегда»). Использование их в jQuery может сделать код более читабельным и позволяет использовать некоторые дополнительные приемы (например, легко добавить второй обратный вызов после запуска ajax-запроса).
bfavaretto

Оба являются асинхронными обратными вызовами
Raynos

1

Отложенные обратные вызовы (иначе как Promices ) позволяют вам писать последовательный асинхронный код без боли и спагетти обратного вызова:

$.when( doAjax(), doAnotherAjax() ).then( haveFunWithMoreAjax ).then( animateStuff );

«когда» позволяет вам ожидать параллельного возврата функций и thenможет быть последовательно соединено.

одно замечание: jQuery deferreds ! = Promices / A , их синтаксис немного другой.

На эту тему есть хорошие статьи: одна в IEBlog, другая в каком-то случайном блоге , книга и популярный вопрос stackoverflow


1
эээ ... получается ... ОП спросил немного другое ... ну, будем надеяться, этот ответ поможет кому-то другому, может быть, когда-нибудь.
c69
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.