Коли я повинен використовувати метод "тоді" відкладеного jQuery і коли я повинен використовувати метод "труба"?


97

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?


1
Я думав про це деякий час, але не можу придумати жодного випадку використання. Це може бути просто накладні витрати на створення нових об'єктів обіцянки, якщо вони вам не потрібні (я не знаю, як вони зв'язані між собою всередині). Тим не менш, є, звичайно, люди, які розуміють це краще, ніж я.
Фелікс Клінг

6
Усіх, хто цікавиться цим питанням, напевно зацікавить Квиток № 11010 на трекері помилок jQuery: Зробити DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A
hippietrail

Відповіді:


103

Оскільки 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, не піддаючи фактичного потоку / порядку викликів.


Взагалі, відкладені об'єкти забезпечують цікавий спосіб декупажу коду :)


Так, я не помітив, що pipeможе фільтрувати те, що thenне може. Але в Googling ці теми, здається, вони вирішили назвати це, pipeа не filterтому, що вважали фільтрацію чимось додатковим бонусом, який прийшов разом із цим, тоді як pipeбільш чітко вказано його справжнє призначення. Тому, схоже, крім фільтрації повинні бути й інші відмінності. (Тоді я знову визнаю, що я не дуже розумію функцію фільтрації навіть у ваших прикладах. Має result values;бути return values;, до речі?)
hippietrail

Коли я кажу, що я не розумію ваших прикладів, чи це щось подібне: у верхньому прикладі вони .then()отримують однакові дані, за resultякими ви фільтруєте щоразу; тоді як у нижньому прикладі .pipe()видаляє деякі дані, що передаються resultперед передачею, як отримають resultдва наступних .then()s?
hippietrail

1
@hippietrail: Тим часом я оновив свою відповідь і також включив інші цілі .pipe(). Якщо зворотний виклик повертає відкладений об'єкт, для цього об’єкта буде зареєстровано подані або виконані помилки зворотного виклику. Я включу ще один приклад. редагувати: щодо вашого другого коментаря: так.
Фелікс Клінг

Отже, одна відмінність полягає в тому, що дані протікають через pipe() той час then(), що більше схожий на вузол листів, в кінці якого ви повинні використовувати свої дані, і він не надходить далі, і що, незважаючи на те, що then()повертається, Deferredвін насправді не використовується / корисний? Якщо це правильно, можливо, допоможе уточнити, як включити щось подібне /* do something with "min"/"max" */до кожного пункту ".then ()".
hippietrail

1
Не хвилюйтесь :) Мені знадобилося певний час, щоб повністю зрозуміти, як працюють відкладені об'єкти та їх методи. Але як тільки ви це зрозумієте, це здається вже не складним. Я згоден, що документація, можливо, може бути написана простішим способом.
Фелікс Клинг

7

Немає жодного випадку, коли Ви ОБОВ'ЯЗКОВО використовуватимете then()його pipe(). Ви завжди можете ігнорувати значення, яке pipe()буде передаватися. Там Можливо, для використання буде незначний удар, pipeале це навряд чи значення.

Тому може здатися, що ви можете просто завжди використовувати їх pipe()в обох випадках. Однак , використовуючиpipe() , ви спілкуєтеся з іншими людьми , читання коду (включаючи себе, через шість місяців) , що є деякий значення для значення, що повертається. Якщо ви відкидаєте це, ви порушуєте цю семантичну конструкцію.

Це все одно, що мати функцію, яка повертає значення, яке ніколи не використовується: заплутане.

Тож використовуйте, then()коли слід, і pipe()коли ви повинні ...


3
Я знайшов приклад із реального життя, використовуючи ці два в блозі К. Скотта Аллена "Експерименти в написанні": Геолокація, Геокодування та jQuery обіцяє : "Тоді логіка управління читається досить добре:" $(function () { $.when(getPosition()) .pipe(lookupCountry) .then(displayResults); }); "Зверніть увагу, що труба відрізняється від то тому, що труба дає нову обіцянку ".
hippietrail

5

Насправді виявляється, що різниця між .then()і .pipe()вважалася непотрібною, і вони стали такими ж, як і у версії 1.8 jQuery.

З коментаряjaubourg у квитку відслідковування помилок jQuery № 11010 "ЗРОБИТИ ЗАМОВЛЕНО. THEN == ВИЗНАЧЕНО. ПІДПОЛУЧИТИ ПРОМИС / A":

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

(моє наголос)


1
Найкраща довідка до цих пір, я шукаю передові звички.
TWiStErRob
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.