Penempatan tangkapan SEBELUM dan SETELAH

103

Saya kesulitan memahami perbedaan antara menempatkan .catchSEBELUMNYA dan SETELAH kemudian dalam janji bertingkat.

Alternatif 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);
});

Alternatif 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);
  });

Perilaku masing-masing fungsi adalah sebagai berikut, test1 gagal jika nomor <0test2 gagal jika nomor > 10dan test3 gagal jika nomor tidak 100. Dalam kasus ini test2 hanya gagal.

Saya mencoba menjalankan dan membuat test2Async gagal, baik SEBELUM dan SETELAH kemudian berperilaku dengan cara yang sama dan itu tidak menjalankan test3Async. Adakah yang bisa menjelaskan kepada saya perbedaan utama dalam menempatkan tangkapan di tempat yang berbeda?

Di setiap fungsi saya console.log('Running test X')untuk memeriksa apakah itu dijalankan.

Pertanyaan ini muncul karena utas sebelumnya yang saya posting Bagaimana mengubah panggilan balik bersarang menjadi janji? . Saya pikir ini adalah masalah yang berbeda dan layak untuk memposting topik lain.

Zanko
sumber
baik .then dan .catch dapat mengubah janji ... jadi saya tidak yakin dari mana asal kesalahpahaman itu. Jika Anda meletakkan catch sebelum .then, itu akan menangkap penolakan yang terjadi sebelum .then dan .then akan menjalankan callback selesai / gagal berdasarkan apa yang terjadi dalam .catch, dan sebaliknya ketika Anda menukarnya.
Kevin B
Maaf jika pertanyaan saya kurang jelas. Tetapi dalam kasus ini seperti yang saya katakan, kedua kasus tersebut berperilaku sama sehingga saya tidak dapat melihat perbedaannya. Bisakah Anda memberi tahu saya kapan kita meletakkan tangkapan SEBELUMNYA dan kapan kita memutuskan untuk meletakkannya SETELAH itu? meletakkannya setelahnya tampak sangat intuitif dan umum. Hanya tidak yakin mengapa kadang-kadang kami meletakkannya sebelum itu
Zanko
Jika mereka melakukan hal yang sama, itu hanya karena apa yang dilakukan masing-masing tidak mengubah hasil dalam kasus khusus ini. Perubahan kecil pada salah satu dari keduanya dapat mengubah hasilnya.
Kevin B
Apa maksud Anda "mengubah hasil". Maaf saya benar-benar bingung haha
Zanko
Misalnya, jika alih-alih melakukan kesalahan Anda tidak melakukan apa pun, janji akan beralih dari ditolak menjadi diselesaikan. Hal itu tentu saja akan mengubah hasilnya, karena janji tersebut sekarang adalah janji yang telah diputuskan, bukan yang ditolak. (kecuali tentu saja itu sudah diselesaikan, dalam hal ini tangkapan tidak akan berjalan)
Kevin B

Jawaban:

237

Jadi, pada dasarnya Anda bertanya apa perbedaan antara keduanya (di mana pjanji dibuat dari beberapa kode sebelumnya):

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

dan

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

Ada perbedaan baik ketika p menyelesaikan atau menolak, tetapi apakah perbedaan itu penting atau tidak tergantung pada apa yang dilakukan kode di dalam .then()atau .catch()penangan.

Apa yang terjadi jika pteratasi:

Dalam skema pertama, saat pdiselesaikan, .then()penangan dipanggil. Jika .then()penangan tersebut mengembalikan nilai atau janji lain yang akhirnya terselesaikan, maka .catch()penangan dilewati. Namun, jika .then()penangan melempar atau mengembalikan sebuah janji yang pada akhirnya menolak, maka .catch()penangan akan mengeksekusi penolakan dalam janji asli p, tetapi juga kesalahan yang terjadi pada .then()penangan.

Dalam skema kedua, saat pdiselesaikan, .then()penangan dipanggil. Jika .then()penangan tersebut melempar atau mengembalikan sebuah janji yang pada akhirnya ditolak, maka .catch()penangan tidak dapat menangkapnya karena itu sebelum itu dalam rantai.

Jadi, itulah perbedaan # 1. Jika .catch()pawang AFTER, maka itu juga bisa menangkap kesalahan di dalam .then()pawang.

Apa yang terjadi jika pditolak:

Sekarang, pada skema pertama, jika promise pditolak, maka .then()handler dilewati dan .catch()handler akan dipanggil seperti yang Anda harapkan. Apa yang Anda lakukan di .catch()pawang menentukan apa yang dikembalikan sebagai hasil akhir. Jika Anda baru saja mengembalikan nilai dari .catch()penangan atau mengembalikan janji yang akhirnya terselesaikan, rantai janji akan beralih ke status terselesaikan karena Anda "menangani" kesalahan dan mengembalikan secara normal. Jika Anda membuang atau mengembalikan janji yang ditolak di .catch()pawang, janji yang dikembalikan tetap ditolak.

Pada skema kedua, jika promise pditolak, maka .catch()handler dipanggil. Jika Anda mengembalikan nilai normal atau janji yang pada akhirnya .catch()diselesaikan dari penangan (dengan demikian "menangani" kesalahan), maka rantai janji beralih ke keadaan terselesaikan dan .then()penangan setelah .catch()akan dipanggil.

Jadi itulah perbedaan # 2. Jika .catch()penangannya SEBELUM, maka ia dapat menangani kesalahan dan memungkinkan .then()penangan untuk tetap dipanggil.

Kapan menggunakan yang:

Gunakan skema pertama jika Anda menginginkan hanya satu .catch()penangan yang dapat menangkap kesalahan baik di promise asli patau di .then()penangan dan penolakan dari pharus melewati .then()penangan.

Gunakan skema kedua jika Anda ingin dapat menangkap error dalam promise asli pdan mungkin (bergantung pada kondisi), izinkan rantai promise untuk dilanjutkan setelah diselesaikan, sehingga menjalankan .then()penangan.

Pilihan lainnya

Ada satu opsi lain untuk menggunakan kedua callback yang dapat Anda teruskan .then()seperti:

 p.then(fn1, fn2)

Ini menjamin bahwa hanya satu dari fn1atau fn2akan pernah dipanggil. Jika berhasil p, maka fn1akan dipanggil. Jika pditolak, maka fn2akan dipanggil. Tidak ada perubahan hasil yang fn1dapat membuat fn2dipanggil atau sebaliknya. Jadi, jika Anda ingin benar-benar memastikan bahwa hanya satu dari dua penangan Anda yang dipanggil terlepas dari apa yang terjadi pada penangan itu sendiri, maka Anda dapat menggunakan p.then(fn1, fn2).

jfriend00
sumber
17
Pertanyaannya secara khusus tentang urutan .then()dan .catch(), yang Anda jawab. Selain itu Anda memberikan beberapa tip kapan harus menggunakan urutan yang mana, yang menurut saya tepat untuk menyebutkan opsi ketiga, yaitu meneruskan sukses dan penanganan kesalahan ke .then () . Dalam kasus itu, paling banyak satu penangan akan dipanggil.
ArneHugo
7
@ArneHugo - Saran yang bagus. Saya tambahkan.
jfriend00
Jadi, selama Perangkaian Janji, bisakah kita menulis .lalu .catch .catch. Lalu jenis skenario?
Kapil Raghuwanshi
@KapilRuwanshi, ya, Anda dapat menggunakannya untuk meneruskan nilai default jika terjadi kegagalan. yaitu Promise.reject(new Error("F")).then(x => x).catch(e => {console.log(e); return [1]}).then(console.log)dan Promise.resolve([2]).then(x => x).catch(e => [1]).then(console.log)
CervEd
1
@DmitryShvedov - Seperti yang saya duga, ini salah .then(this.setState({isModalOpen: false})). Anda tidak meneruskan referensi fungsi .then()agar kode di dalam tanda kurung segera dieksekusi (sebelum janji diselesaikan). Seharusnya begitu .then(() => this.setState({isModalOpen: false})).
jfriend00
31

Jawaban jfriend00 sangat bagus, tetapi saya pikir akan menjadi ide yang baik untuk menambahkan kode sinkron analog.

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

mirip dengan sinkron:

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

Jika iMightThrow()tidak melempar, then()akan dipanggil. Jika ia melempar (atau jika then()dirinya sendiri melempar), maka handleCatch()akan dipanggil. Perhatikan bagaimana catchblok tidak memiliki kendali atas pemanggilan atau tidak then.

Di samping itu,

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

mirip dengan sinkron:

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

then()

Dalam hal ini, jika iMightThrow()tidak melempar, maka then()akan dieksekusi. Jika tidak melempar, maka akan terserah untuk handleCatch()memutuskan apakah then()dipanggil, karena jika handleCatch()ditarik kembali, maka then()tidak akan dipanggil, karena pengecualian akan segera dilemparkan ke pemanggil. Jika handleCatch()dapat menangani masalah dengan anggun, maka then()akan dipanggil.

akivajgordon.dll
sumber
ini adalah penjelasan yang bagus tapi Anda bisa membungkus anak yatim piatu itu then()dalamfinally{...}
tyskr
2
@ 82Tuskers, Apakah Anda yakin? Jika saya menempatkan then()di finally{...}, akan tidak tidak benar disebut bahkan jika handleCatch()melempar? Perlu diingat bahwa tujuan saya adalah untuk menunjukkan kode sinkron yang analog, bukan untuk menyarankan cara penanganan pengecualian yang berbeda
akivajgordon
Jadi, jika kita ingin menangani semua kasus tetapi masih berantai .then () apakah lebih baik menggunakan .then (lakukan sesuatu) .catch (log err dan update state) .then (lakukan hal lain) .catch (log err) di mana kami mencoba untuk menangkap setiap titik tetapi juga terus mengeksekusi stmnts?
anna