Розміщення улову до і після того


103

У мене виникають проблеми з розумінням різниці між тим, як .catchВПЕРЕД та ПІСЛЯ потім вкластися.

Альтернатива 1:

test1Async(10).then((res) => {
  return test2Async(22)
    .then((res) => {
      return test3Async(100);
    }).catch((err) => {
      throw "ERROR AFTER THEN";
    });
}).then((res) => {
  console.log(res);
}).catch((err) => {
  console.log(err);
});

Альтернатива 2:

test1Async(10).then((res) => {
   return test2Async(22)
     .catch((err) => {
        throw "ERROR BEFORE THEN";
      })
      .then((res) => {
        return test3Async(100);
      });
  }).then((res) => {
    console.log(res);
  }).catch((err) => {
    console.log(err);
  });

Поведінка кожної функції полягає в наступному, test1 <0виходить з ладу, якщо число test2 не вдається, якщо число є, > 10а test3 не вдається, якщо число немає 100. У цьому випадку тест2 лише провалюється.

Я спробував запустити і зробити тест test2Async не вдається, і перед, і після цього поводиться однаково, і це не виконує test3Async. Чи може хтось пояснити мені головну різницю розміщення улову в різних місцях?

У кожній функції я console.log('Running test X')перевіряю, чи виконується вона.

Це питання виникає через попередню тему, яку я розмістив Як перетворити вкладений зворотний виклик у обіцянку? . Я вважаю, що це інша проблема, і варто написати іншу тему.


і тоді, і .catch можуть змінити обіцянку ... тож я не впевнений, звідки походить неправильне розуміння. Якщо ви поставите улов перед .then, він буде відхиляти випадок, який відбувся до .then і .then запустить це зроблено / відмовить зворотні виклики на основі того, що відбувається в .catch, і навпаки, коли ви поміняєте їх.
Кевін Б

Вибачте, якщо моє запитання не було зрозумілим. Але в цьому випадку, як я вже сказав, обидва випадки поводяться однаково, тому я не бачу різниці. Чи можете ви сказати мені, коли ми ставимо ловити ПЕРЕД, і коли ми вирішили поставити його ПІСЛЯ? викладення цього виглядає справді інтуїтивно зрозумілим і поширеним. Просто не впевнений, чому іноді ми ставимо це раніше
Занко

Якщо вони виконують те саме, це просто тому, що те, що кожен робить, не змінює результат у цьому конкретному випадку. Незначна зміна будь-якого може змінити результат.
Кевін Б

Що ви маєте на увазі "змінити результат". Вибачте, я справді плутаю ха-ха
Занко

Наприклад, якщо замість того, щоб викинути помилку, ви просто нічого не зробили, обіцянка перемкнеться від відхилення до вирішення. Це, звичайно, змінило б результат, оскільки тепер обіцянка - це вирішена, а не відхилена. (якщо, звичайно, це вже не було вирішено; в такому випадку вилов все-таки не запустився б)
Кевін Б

Відповіді:


237

Отже, ви в основному запитуєте, в чому різниця між цими двома (де pобіцянка, створена з попереднього коду):

return p.then(...).catch(...);

і

return p.catch(...).then(...);

Існують відмінності або тоді, коли p вирішує чи відхиляє, але чи важливі ці відмінності чи ні, залежить від того, що робить код всередині .then()або .catch()обробники.

Що відбувається при pвирішенні:

У першій схемі при pвирішенні .then()викликається обробник. Якщо цей .then()обробник поверне значення або інша обіцянка, яка врешті-решт вирішиться, .catch()обробник пропускається. Але, якщо .then()обробник або кидає, або повертає обіцянку, яка врешті-решт відхиляє, .catch()обробник виконує як відхилення в початковій обіцянці p, так і помилку, яка виникає в .then()обробнику.

У другій схемі при pвирішенні .then()викликається обробник. Якщо цей .then()обробник або кидає, або повертає обіцянку, яка врешті-решт відхиляє, .catch()обробник не може це зрозуміти, тому що це перед цим у ланцюжку.

Отже, це різниця №1. Якщо .catch()обробник знаходиться ПІСЛЯ, то він також може виявити помилки всередині .then()обробника.

Що відбувається при pвідхиленні:

Тепер у першій схемі, якщо обіцянка pвідхилиться, .then()обробник пропущений і .catch()обробник буде викликаний так, як ви очікували. Те, що ви робите в .catch()обробнику, визначає, що повертається як кінцевий результат. Якщо ви просто повертаєте значення від .catch()обробника або повертаєте обіцянку, яка врешті-решт вирішиться, ланцюг обіцянок переходить у вирішений стан, оскільки ви "обробили" помилку і повернулися нормально. Якщо ви кинете або повернете відхилену обіцянку в .catch()обробнику, то повернута обіцянка залишається відхиленою.

У другій схемі, якщо обіцянку pвідхиляє, тоді .catch()викликається обробник. Якщо ви повернете нормальне значення або обіцянку, яка врешті-решт вирішиться з .catch()обробника (таким чином "обробляючи" помилку), ланцюг обіцянок переходить у вирішений стан, і .then()обробник після заклику .catch()буде викликаний.

Отже, це різниця №2. Якщо .catch()обробник є ДО ПЕРЕД, то він може виправити помилку і дозволити .then()оброблювачеві все ще викликати.

Коли користуватися якими:

Скористайтеся першою схемою, якщо ви хочете лише один .catch()обробник, який може виявити помилки або в оригінальній обіцянці, pабо в .then()обробнику, і відхилення від pповинно пропустити .then()обробник.

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

Інший варіант

Є ще один варіант використання обох зворотних викликів, на які можна перейти, .then()як у:

 p.then(fn1, fn2)

Це гарантує, що тільки один fn1або fn2коли-небудь буде викликаний. Якщо pвирішує, то fn1буде викликано. Якщо pвідхиляє, тоді fn2буде викликано. Ніяка зміна результату fn1ніколи не fn2може викликати дзвінки або навпаки. Отже, якщо ви хочете зробити абсолютно впевненим, що викликається лише один з двох ваших обробників, незалежно від того, що відбувається в самих обробниках, тоді ви можете використовувати p.then(fn1, fn2).


17
Питання стосується конкретно порядку .then()та .catch(), на яке ви відповідаєте. Крім того, ви даєте кілька порад, коли користуватися тим замовленням, де я вважаю, що доречно згадати третій варіант, а саме передати як обробник успіху, так і помилку в .then () . У такому випадку буде викликано не більше одного обробника.
ArneHugo

7
@ArneHugo - Гарна пропозиція. Я додав.
jfriend00

Отже, під час Обіцяння ланцюжка ми можемо писати .then .catch .catch. Тоді різновиди сценаріїв?
Kapil Raghuwanshi

@KapilRaghuwanshi, так, ви можете використовувати його для передачі значення за замовчуванням у разі відмови. тобто Promise.reject(new Error("F")).then(x => x).catch(e => {console.log(e); return [1]}).then(console.log)і Promise.resolve([2]).then(x => x).catch(e => [1]).then(console.log)
CervEd

1
@DmitryShvedov - Як я здогадався, це неправильно .then(this.setState({isModalOpen: false})). Ви не передаєте посилання на функцію, щоб .then()код у паролях виконувався негайно (до того, як обіцянка вирішиться). Це повинно бути .then(() => this.setState({isModalOpen: false})).
jfriend00

31

Відповідь jfriend00 відмінна, але я подумав, що було б хорошою ідеєю додати аналогічний синхронний код.

return p.then(...).catch(...);

схожий на синхронний:

try {
  iMightThrow() // like `p`
  then()
} catch (err) {
  handleCatch()
}

Якщо iMightThrow()не кине, then()буде викликано. Якщо він кидає (або якщо then()сам кидає), то handleCatch()буде називатися. Зверніть увагу, як catchблок не має контролю над тим, thenвикликається чи ні .

З іншої сторони,

return p.catch(...).then(...);

схожий на синхронний:

try {
  iMightThrow()
} catch (err) {
  handleCatch()
}

then()

У цьому випадку, якщо iMightThrow()не кине, то then()виконає. Якщо він все-таки кине, то було б handleCatch()вирішити, чи then()буде викликано, тому що якщо handleCatch()повторно відкидається, то then()не буде викликано, оскільки виняток буде кинутий абоненту негайно. Якщо ви handleCatch()зможете витончено впоратися з проблемою, тоді вам then()буде зателефоновано.


це хороше пояснення, але ви можете загорнути сироту then()вfinally{...}
тискр

2
@ 82Tuskers, ви впевнені? Якщо я ставлю then()в finally{...}, що не буде його неправильно назвати , навіть якщо handleCatch()кидки? Майте на увазі, що моєю метою було показати аналогічний синхронний код, а не пропонувати різні способи обробки винятків
akivajgordon

Отже, якщо ми хочемо обробити всі випадки, але все-таки ланцюжок .then (), найкраще використовувати. де ми намагаємося зловити в кожній точці, але також продовжуємо виконувати stmnts?
Анна
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.