Когда я должен использовать метод "then" в jQuery deferred, а когда - "pipe"?

JQuery-х 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 ?

3 ответа

Решение

С jQuery 1.8 .then ведет себя так же, как .pipe:

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

а также

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

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


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

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

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

    Это не так с .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 без раскрытия фактического потока / порядка вызовов.


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

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

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

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

Так что используйте then() когда вы должны, и pipe() когда ты должен...

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

Из комментария jaubourg в билете трекера ошибок jQuery # 11010 "MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A":

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

(emphassis мой)

Другие вопросы по тегам