Dengan Janji, mengapa browser mengembalikan penolakan dua kali tetapi tidak menyelesaikan dua kali?

10

Saya mengalami kesulitan memahami javaScript promises. Saya menulis kode berikut:

var p = new Promise(function(resolve,reject){

    reject(Error("hello world"));
});

setTimeout(()=>p.catch(e=>console.log(e)),5000);

Saya segera melihat ini di konsol pengembang Chrome saya: masukkan deskripsi gambar di sini

Tetapi setelah saya menunggu 5 detik, pesan secara otomatis berubah menjadi hitam seperti gambar ini: masukkan deskripsi gambar di sini

Saya belum pernah melihat perilaku ini sebelumnya antara kode javaScript saya dan konsol pengembang, di mana kode javaScript saya dapat "memodifikasi konten yang ada" di konsol pengembang.

Jadi saya memutuskan untuk melihat apakah situasi yang sama terjadi dengan resolvemenulis kode ini:

var p = new Promise(function(resolve,reject){

    resolve("hello world");
});

setTimeout(()=>p.then(e=>console.log(e)),5000);

Tetapi dalam situasi ini, konsol pengembang saya tidak menunjukkan apa-apa sampai 5 detik kemudian, yang kemudian dicetak hello world.

Mengapa resolvedan rejectdiperlakukan begitu berbeda dalam hal ketika mereka dipanggil?


TAMBAHAN

Saya juga menulis kode ini:

var p = new Promise(function(resolve,reject){

    reject(Error("hello world"));
});

setTimeout(()=>p.catch(e=>console.log("errors",e)),5000);
setTimeout(()=>p.catch(e=>console.log("errors 2",e)),6000);
setTimeout(()=>p.catch(null),7000);

Ini menyebabkan beberapa keluaran ke konsol pengembang. Kesalahan merah pada waktu 0, merah berubah menjadi hitam pada waktu 5 detik dengan teks errors hello world, kemudian pesan kesalahan baru pada waktu 6 detik errors 2 hello world, kemudian pesan kesalahan merah pada waktu 7 detik. Sekarang saya sangat bingung berapa kali rejectbenar-benar dipanggil .... Saya tersesat ...

John
sumber
1
Sebentar lagi: var p = new Promise(function(resolve,reject){ reject(Error("hello world")); });bisa ditulis secara lebih idiomatis dan ringkas sebagai var p = Promise.reject(Error("hello world"));:-)
TJ Crowder
1
Pertanyaan yang luar biasa
TJ Crowder

Jawaban:

11

Wow, itu sangat keren. Saya belum pernah melihat konsol melakukan itu sebelumnya. (Ini memiliki bentuk lain dari perilaku dinamis, jadi ...) Inilah yang terjadi:

Dalam kasus pertama, eksekusi kode dari segala sesuatu di luar setTimeoutkode callback Anda selesai dan tumpukan eksekusi kembali sehingga hanya " kode platform " (seperti yang dijanjikan oleh spec / A +), yang sedang berjalan, bukan kode JavaScript pengguna lahan (untuk saat ini). Pada saat itu, janji itu ditolak dan tidak ada yang ditangani penolakan, jadi itu adalah penolakan tertangani dan devtools melaporkan kepada Anda seperti itu.

Kemudian , lima detik kemudian, panggilan balik Anda berjalan dan menempel handler penolakan. Pada titik ini, penolakan tidak lagi ditangani. Tampaknya, Chrome / V8 / devtools bekerja bersama untuk menghapus peringatan penolakan yang tidak ditangani dari konsol. Apa yang Anda lihat muncul sebagai gantinya adalah apa yang Anda hasilkan di handler penolakan melalui console.log. Jika Anda memasang reler handler lebih awal, Anda tidak akan mendapatkan kesalahan penolakan yang tidak ditangani.

Ini tidak terjadi dengan pemenuhan karena tidak menangani pemenuhan bukanlah kondisi kesalahan. Tidak menangani penolakan.

TJ Crowder
sumber
1
Oh, itu masuk akal. Saya perhatikan FireFox menanganinya sedikit berbeda. Tapi ok, lebih masuk akal sekarang.
Yohanes
1
Saya menulis jawaban yang sama, tetapi SO memuat milik Anda, jadi saya belum memposting milik saya. Penjelasan yang bagus! +1
FZ