Когда я должен использовать метод jQuery deferred «then» и когда мне следует использовать метод «pipe»? - PullRequest
94 голосов
/ 06 марта 2012

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?

Ответы [ 3 ]

102 голосов
/ 07 марта 2012

Поскольку 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, не раскрывая фактический поток / порядок вызовов.


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

6 голосов
/ 18 июня 2012

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

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

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

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

5 голосов
/ 07 августа 2012

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

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

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

(emphassis mine)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...