javascript - Mengapa Penolakan Janji ES6 saya perlu digunakan dengan segera untuk mengelakkan mesej ralat konsol?
Sila ambil perhatian: Berikut adalah isu yang berkelakuan berbeza pada pelayar yang berbeza. Jadi mungkin ini adalah isu pelaksanaan pelayar. Saya ingin nasihat tidak kira apa pun.
Dalam aplikasi saya, saya sedang membuat beberapa janji yang mungkin tidak saya gunakan sehingga beberapa waktu di masa depan. Itu sepatutnya tidak menjadi masalah, mereka adalah janji, lagipun.
Jika janji yang disimpan itu diselesaikan, tiada masalah. Saya boleh menggunakannya pada bila-bila masa di masa depan yang saya mahu, dan sebanyak mana kali yang saya mahu. Seperti yang dijangkakan.
Walau bagaimanapun, jika janji yang disimpan itu ditolak, terdapat masalah. Melainkan saya mengatasi penolakan itu tidak lama selepas ia dibuat (tidak pasti berapa lama), mesej konsol akan muncul dalam Chrome atau Firefox yang menunjukkan bahawa terdapat penolakan/error janji yang tidak ditangkap. IE tidak memaparkan ralat itu.
Jadi pertimbangkan kod berikut:
console.log("About to make a promise rejection.");
var foo = Promise.reject(new Error("Foo Rejected Promise."));
console.log("Promise rejection made.");
Perlu diambil perhatian bahawa tiada penggunaan atau pembaziran janji foo. Ia hanya disimpan untuk kegunaan pada masa hadapan.
Konsol pada IE kelihatan seperti ini:
Sedang hendak menolak janji.
Penolakan janji dibuat.
Yang dijangkakan. Namun, kod yang sama di Chrome akan menghasilkan konsol berikut:
Akan membuat penolakan janji.
Penolakan janji dibuat.
Ralat tidak ditangkap (dalam janji): Foo Menolak Janji.(…)
Firefox kelihatan hampir sama seperti Chrome, selain daripada perkataan di sekitar ralat "uncaught".
Tetapi perkara yang ingin saya lakukan adalah saya berniat untuk menguruskan "ralat" ini kemudian, pada masa saya menggunakan janji itu. Hanya kerana MEMILIKI janji yang ditolak tidak sepatutnya menyebabkan ralat konsol..... itu sepatutnya berlaku jika saya menggunakan janji itu dan tidak menguruskan ralat tersebut.
Untuk mensimulasikan pengendalian ralat "jauh kemudian", pertimbangkan pengubahsuaian kod ini:
console.log("About to make a promise rejection.");
var foo = Promise.reject(new Error("Foo Rejected Promise."));
console.log("Promise rejection made.");
setTimeout(() => {
foo.catch((err) => {
console.log("Displaying " + err.message + " 10 seconds later.");
});
}, 10000);
Sekarang dalam kes ini, kami 'mengendalikan' ralat dengan memaparkan sesuatu di konsol selepas masa tamat. Kini IE masih mengendalikannya seperti yang saya jangkakan:
Sedang hendak membuat penolakan janji.
Penolakan janji telah dibuat.
Paparan Janji Foo Ditolak. 10 saat kemudian.
Dalam kes ini, Firefox berkelakuan seperti IE, dan memaparkan mesej-mesej ini dengan tepat, dan tidak memberikan saya ralat konsol yang salah.
Walau bagaimanapun, Chrome masih memberikan ralat yang salah:
Sedang bersedia untuk menolak janji.
Penolakan janji dibuat.
Ralat tidak ditangkap (dalam janji): Foo Menolak Janji.(…)
Paparan Janji Foo Ditolak. 10 saat kemudian.
Jadi Chrome mengadu tentang ralat saya yang tidak ditangani, dan kemudian memaparkan bahawa ia telah ditangani.
Nampaknya saya boleh mengatasi semua ini dengan kod berikut yang kelihatan seperti helah. Pada dasarnya saya melakukan pengendalian 'palsu' bagi ralat apabila janji dibuat dan kemudian benar-benar menanganinya cara yang saya mahukan kemudian:
console.log("About to make a promise rejection.");
var foo = Promise.reject(new Error("Foo Rejected Promise."));
foo.catch(() => { }); // Added this hack-ish code.
console.log("Promise rejection made.");
setTimeout(() => {
foo.catch((err) => {
console.log("Displaying " + err.message + " 10 seconds later.");
});
}, 10000);
Tetapi ini adalah kod yang hodoh.
Soalan saya adalah dua perkara - adakah ada cara untuk melihat apa yang Chrome (dan sehingga tahap tertentu FireFox) sedang lakukan dan menganggapnya sebagai satu kebaikan? Kerana bagi saya ia nampak teruk. Dan kedua, adakah terdapat cara yang lebih baik untuk mengatasi ini daripada helah berpura-pura mengendalikan ralat apabila anda sebenarnya tidak mengendalikannya?
Terima kasih terlebih dahulu.
Jawapan #1
Mana-mana janji yang berpeluang ditolak, harus ditangani, sama seperti pengecualian.
Tidak begitu banyak kemungkinan bahawa janji tidak akan disambungkan secara segerak. Tetapi jika ia tidak berlaku, ia memerlukan .catch(() => { }) yang 'hack-ish' untuk menangani penolakan secara segerak.
Tingkah laku penolakan janji Chrome meletakkan ini
var foo = Promise.reject(new Error("Foo Rejected Promise."));
foo.catch(() => { }); // Added this hack-ish code.
setTimeout(() => {
foo.catch((err) => {
console.log("Displaying " + err.message + " 10 seconds later.");
});
}, 10000);
ke dalam bot pengendalian ralat yang sama seperti
try {
throw new Error("Foo"));
setTimeout(() => {
try {
throw new Error("Foo"));
} catch (err) {
console.log("Displaying " + err.message + " 10 seconds later.");
}
}, 10000);
} catch (err) {}
Bukankah pengecualian akan menunggu 10 saat untuk ditangkap oleh try...catch seterusnya sebelum dilemparkan? Ia tidak akan menunggu walau sesaat pun. Ia memerlukan blok try { ... } catch (err) {} yang 'like hack' untuk berfungsi seperti yang dimaksudkan.
Tingkah laku ini sudah dikenal baik oleh pengguna perpustakaan Bluebird untuk jangka masa yang agak lama. Bluebird mempunyai pengendalian ralat yang boleh disesuaikan dan membolehkan penyahpepijatan janji dengan berkesan walaupun pada skala besar.
Perilaku ini juga dikenali dalam pembangunan Angular 2, ia dipaksa untuk janji dengan perpustakaan Zone.js.
Memandangkan pengesanan ralat janji mempunyai nilai yang terhad dan terpakai kepada persekitaran pembangunan, adalah mungkin untuk mengubah suai atau mematikan tingkah laku ini untuk janji asli dalam binaan pengeluaran:
if (typeof DEBUG_PROMISE === 'undefined') {
window.addEventListener('unhandledrejection', (e) => {
e.preventDefault();
console.warn(e.reason);
});
}
Berikut adalah lebih banyak bacaan mengenai subjek ini.
Jawapan #2
Sebagai seseorang yang telah bekerja dengan janji selama beberapa tahun sekarang, saya faham mengapa tingkah laku ini ditetapkan dalam Chrome. Mudah untuk lupa menangkap janji yang ditolak, dan jika terdapat ralat dalam .then(), tiada notifikasi akan diberikan dan program anda hanya akan kekal senyap. Ia boleh menjadi sangat sukar untuk menyahpepijat.
function throwException(c){ return a.b = 2 ;} //a is not defined. throws exception.
let p = q.resolve("a")
.then(x => x + "b")
.then(y => throwException(y))
.then(z=> z+"c"); //this will say nothing in Q library
ini boleh dengan mudah mewujudkan tingkah laku yang sangat samar apabila seseorang terlupa untuk menangkap ralat.
Dalam perpustakaan Q, anda mempunyai fungsi .done() untuk benar-benar menguatkuasakan apa yang dilakukan oleh Chrome di sini:
let p = q.resolve("a")
.then(x => x + "b")
.then(y => throwException(y))
.then(z=> z+"c")
.done(); //this will throw an exception.
Jadi, ini adalah masalah yang diketahui dalam janji (promises). Chrome telah memutuskan untuk menjadikan .done() sebagai tingkah laku lalai, meminta anda secara eksplisit menggunakan .catch(_=>{}) jika anda ingin mengabaikan janji yang gagal. Q telah memutuskan untuk menjadikan kegagalan senyap sebagai lalai, meminta anda untuk secara eksplisit menggunakan .done() jika anda ingin berlaku pengecualian pada janji yang gagal. Jika anda menyukai pendekatan yang terakhir, anda sentiasa boleh menyertakan perpustakaan janji. Ia adalah soal citarasa menurut pandangan saya.
