Когда следует использовать метод then then jQuery «then» и когда следует использовать метод «pipe»?

У jQuery’s Deferred есть две функции, которые могут использоваться для реализации асинхронной цепочки функций:

then()

deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred

doneCallbacks Функция или массив функций, вызываемых при разрешении Отложенного.
failCallbacks Функция или массив функций, вызываемых при отклонении Отложенного.

pipe()

deferred.pipe( [doneFilter] [, failFilter] ) Returns: Promise

doneFilter Необязательная функция, вызываемая при разрешении Отложенного.
failFilter Необязательная функция, вызываемая при отклонении Отложенного.

Я знаю, что then() было вокруг немного дольше, чем pipe() поэтому последнее должно добавить дополнительную выгоду, но какая разница именно ускользает от меня. Оба имеют практически одинаковые параметры обратного вызова, хотя они различаются по названию, и разница между возвратом Deferred и возвратом Promise кажется незначительной.

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

Итак, когда лучше использовать then и когда лучше использовать pipe ?


прибавление

Отличный ответ Феликса действительно помог прояснить, как эти две функции отличаются. Но мне интересно, есть ли моменты, когда функция then() предпочтительнее функциональности pipe() .

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

Но есть ли прецедент, который требует, чтобы then() возвратил оригинал Deferred который не может быть выполнен с помощью pipe() из-за того, что он возвращает новое Promise ?

    Поскольку jQuery 1.8. .then ведет себя так же, как .pipe :

    Уведомление об изнашивании: с jQuery 1.8 метод deferred.pipe() устарел. Вместо этого следует использовать метод deferred.then() , который заменяет его.

    а также

    Начиная с jQuery 1.8 , метод deferred.then() возвращает новое promise, которое может фильтровать статус и значения отложенных через функцию, заменяя теперь устаревший метод deferred.pipe() .

    Приведенные ниже примеры могут быть полезны для некоторых.


    Они служат различным целям:

    • .then() следует использовать всякий раз, когда вы хотите работать с результатом процесса, то есть, как указано в документации, когда отложенный объект разрешен или отклонен. Это то же самое, что использовать .done() или .fail() .

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

      Это не относится к .then() (или .done() , .fail() ), возвращаемые значения зарегистрированных обратных вызовов просто игнорируются.

    Таким образом, вы не используете либо .then() либо .pipe() . Вы можете использовать .pipe() для тех же целей, что и .then() но обратное не выполняется.


    Пример 1

    Результатом некоторой операции является массив объектов:

     [{value: 2}, {value: 4}, {value: 6}] 

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

     deferred.then(function(result) { // result = [{value: 2}, {value: 4}, {value: 6}] var values = []; for(var i = 0, len = result.length; i < len; i++) { values.push(result[i].value); } var min = Math.min.apply(Math, values); /* do something with "min" */ }).then(function(result) { // result = [{value: 2}, {value: 4}, {value: 6}] var values = []; for(var i = 0, len = result.length; i < len; i++) { values.push(result[i].value); } var max = Math.max.apply(Math, values); /* do something with "max" */ }); 

    В обоих случаях вам нужно перебирать список и извлекать значение из каждого объекта.

    Не было бы лучше каким-то образом извлечь значения заранее, чтобы вам не приходилось делать это в обоих обратных вызовах индивидуально? Да! И это то, что мы можем использовать .pipe() для:

     deferred.pipe(function(result) { // result = [{value: 2}, {value: 4}, {value: 6}] var values = []; for(var i = 0, len = result.length; i < len; i++) { values.push(result[i].value); } return values; // [2, 4, 6] }).then(function(result) { // result = [2, 4, 6] var min = Math.min.apply(Math, result); /* do something with "min" */ }).then(function(result) { // result = [2, 4, 6] var max = Math.max.apply(Math, result); /* do something with "max" */ }); 

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


    Пример 2.

    Рассмотрим Ajax-вызовы. Иногда вы хотите инициировать один вызов Ajax после завершения предыдущего. Один из способов - сделать второй вызов внутри done обратного вызова:

     $.ajax(...).done(function() { // executed after first Ajax $.ajax(...).done(function() { // executed after second call }); }); 

    Теперь давайте предположим, что вы хотите развязать свой код и поместить эти два вызова Ajax внутри функции:

     function makeCalls() { // here we return the return value of `$.ajax().done()`, which // is the same deferred object as returned by `$.ajax()` alone return $.ajax(...).done(function() { // executed after first call $.ajax(...).done(function() { // executed after second call }); }); } 

    Вы хотите использовать отложенный объект, чтобы разрешить другой код, который вызывает makeCalls для присоединения обратных вызовов для второго вызова Ajax, но

     makeCalls().done(function() { // this is executed after the first Ajax call }); 

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

    Решением было бы использовать .pipe() вместо:

     function makeCalls() { // here we return the return value of `$.ajax().pipe()`, which is // a new deferred/promise object and connected to the one returned // by the callback passed to `pipe` return $.ajax(...).pipe(function() { // executed after first call return $.ajax(...).done(function() { // executed after second call }); }); } makeCalls().done(function() { // this is executed after the second Ajax call }); 

    Используя .pipe() вы теперь можете добавлять обратные вызовы к внутреннему вызову Ajax, не подвергая действительный stream / порядок вызовов.


    В общем, отложенные объекты предоставляют интересный способ разделить ваш код 🙂

    Нет случая, когда вы ДОЛЖНЫ использовать then() над pipe() . Вы всегда можете игнорировать значение, которое будет проходить через pipe() . Может быть небольшой удар по производительности для использования pipe но это вряд ли имеет значение.

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

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

    Поэтому используйте then() когда вам нужно, и pipe() когда вам нужно …

    На самом деле получается, что разница между .then() и .pipe() считается ненужной, и они были сделаны такими же, как и для jQuery версии 1.8.

    Из комментария jaubourg в билете для отслеживания ошибок jQuery # 11010 «СДЕЛАЙТЕ DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A»:

    В 1.8 мы удалим старое и заменим его текущим трубой. Но самым грустным следствием является то, что мы должны будем сказать людям, чтобы они использовали нестандартные, неудачные и прогрессивные, потому что предложение не обеспечивает простой, ЭФФЕКТИВНЫЙ, означает просто добавить обратный вызов.

    (шахта Эммассос)