Mesin JavaScript: bagaimana cara kerjanya? Dari tumpukan panggilan ke janji-janji, (hampir) semua yang perlu Anda ketahui


Pernahkah Anda bertanya-tanya bagaimana browser membaca dan menjalankan kode JavaScript? Itu terlihat misterius, tetapi dalam posting ini Anda bisa mendapatkan ide tentang apa yang terjadi di bawah tenda.

Kami memulai perjalanan kami ke dalam bahasa dengan bertualang ke dunia mesin JavaScript yang indah.

Buka konsol di Chrome dan buka tab Sources. Anda akan melihat beberapa bagian, dan salah satu yang paling menarik disebut Call Stack (di Firefox Anda akan melihat Call Stack ketika Anda meletakkan breakpoint dalam kode):



Apa itu tumpukan panggilan? Tampaknya ada banyak hal yang terjadi di sini, bahkan demi mengeksekusi beberapa baris kode. Sebenarnya, JavaScript tidak datang dalam kotak dengan setiap browser. Ada komponen besar yang mengkompilasi dan menginterpretasikan kode JavaScript kami - ini adalah mesin JavaScript. Yang paling populer adalah V8, digunakan di Google Chrome dan Node.js, SpiderMonkey di Firefox, JavaScriptCore di Safari / WebKit.

Mesin JavaScript saat ini adalah contoh hebat dari rekayasa perangkat lunak, dan hampir tidak mungkin untuk berbicara tentang semua aspek. Namun, pekerjaan utama pada eksekusi kode dilakukan untuk kita oleh hanya beberapa komponen mesin: Call Stack (panggilan stack), Memori Global (memori global) dan Konteks Eksekusi (konteks eksekusi). Siap bertemu mereka?

Konten:

  1. Mesin JavaScript dan memori global
  2. Mesin JavaScript: bagaimana cara kerjanya? Konteks eksekusi global dan tumpukan panggilan
  3. JavaScript adalah single-threaded dan cerita menyenangkan lainnya
  4. JavaScript tidak sinkron, antrean panggilan balik dan loop acara
  5. Telepon balik neraka dan menjanjikan ES6
  6. Membuat dan bekerja dengan Janji JavaScript
  7. Penanganan Kesalahan dalam Janji ES6
  8. ES6 Combiseator Promise: Promise.all, Promise.allSettled, Promise.any dan lainnya
  9. ES6 menjanjikan dan antrian mikrotask
  10. Mesin JavaScript: bagaimana cara kerjanya? Evolusi asinkron: dari janji menjadi asinkron / menunggu
  11. Mesin JavaScript: bagaimana cara kerjanya? Ringkasan

1. Mesin JavaScript dan memori global


Saya mengatakan bahwa JavaScript adalah bahasa yang dikompilasi dan ditafsirkan. Percaya atau tidak, mesin JavaScript sebenarnya mengkompilasi mikrodetik kode Anda sebelum dijalankan.

Semacam sihir, ya? Sihir ini disebut JIT (Kompilasi tepat waktu). Itu saja adalah topik besar diskusi, bahkan buku tidak akan cukup untuk menggambarkan karya JIT. Tetapi untuk sekarang, kita akan melewatkan teori dan fokus pada fase eksekusi, yang tidak kalah menarik.

Untuk memulai, lihat kode ini:

var num = 2; function pow(num) { return num * num; } 

Misalkan saya bertanya kepada Anda bagaimana kode ini diproses di browser? Apa yang akan kamu jawab? Anda dapat mengatakan: "browser membaca kode" atau "browser mengeksekusi kode". Pada kenyataannya, semuanya tidak begitu sederhana. Pertama, kode dibaca bukan oleh browser, tetapi oleh mesin. Mesin JavaScript membaca kode , dan segera setelah mendefinisikan baris pertama, ia menempatkan beberapa tautan ke dalam memori global .

Memori global (juga disebut tumpukan) adalah area di mana mesin JavaScript menyimpan variabel dan deklarasi fungsi. Dan ketika dia membaca kode di atas, dua pengikat akan muncul di memori global:



Bahkan jika contoh hanya berisi variabel dan fungsi, bayangkan kode JavaScript Anda dijalankan di lingkungan yang lebih besar: di browser atau di Node.js. Dalam lingkungan seperti itu, ada banyak fungsi dan variabel yang telah ditentukan yang disebut global. Oleh karena itu, memori global akan mengandung lebih banyak data daripada sekadar num dan pow , perlu diingat.

Tidak ada yang berjalan saat ini. Sekarang mari kita coba menjalankan fungsi kita:

 var num = 2; function pow(num) { return num * num; } pow(num); 

Apa yang akan terjadi Dan sesuatu yang menarik akan terjadi. Saat memanggil fungsi, mesin JavaScript akan menyoroti dua bagian:

  • Konteks Eksekusi Global
  • Tumpukan panggilan

Apa mereka

2. Mesin JavaScript: bagaimana cara kerjanya? Konteks eksekusi global dan tumpukan panggilan


Anda belajar bagaimana mesin JavaScript membaca variabel dan deklarasi fungsi. Mereka jatuh ke dalam memori global (tumpukan).

Tapi sekarang kami sedang menjalankan fungsi JavaScript, dan mesin harus menangani ini. Bagaimana? Setiap mesin JavaScript memiliki komponen kunci yang disebut tumpukan panggilan .

Ini adalah struktur data bertumpuk : elemen dapat ditambahkan ke atasnya dari atas, tetapi mereka tidak dapat dikecualikan dari struktur sementara ada elemen lain di atasnya. Ini adalah cara kerja fungsi JavaScript. Pada eksekusi, mereka tidak dapat meninggalkan tumpukan panggilan jika ada fungsi lain di dalamnya. Perhatikan hal ini, karena konsep ini membantu memahami pernyataan "JavaScript berurutan tunggal."

Tetapi kembali ke contoh kita. Ketika suatu fungsi dipanggil, mesin mengirimkannya ke tumpukan panggilan :



Saya suka menyajikan tumpukan panggilan sebagai tumpukan chip Pringles. Kita tidak bisa makan keripik dari dasar tumpukan sampai kita makan yang ada di atas. Untungnya, fungsi kami sinkron: itu hanya perkalian yang cepat dihitung.

Pada saat yang sama, mesin menempatkan konteks eksekusi global dalam memori, ini adalah lingkungan global di mana kode JavaScript dieksekusi. Begini tampilannya:



Bayangkan konteks eksekusi global dalam bentuk lautan di mana fungsi JavaScript global mengambang seperti ikan. Manis sekali! Tapi ini baru setengah dari cerita. Bagaimana jika fungsi kita memiliki variabel bersarang atau fungsi internal?

Bahkan dalam kasus sederhana, seperti yang ditunjukkan di bawah ini, mesin JavaScript membuat konteks eksekusi lokal :

 var num = 2; function pow(num) { var fixed = 89; return num * num; } pow(num); 

Perhatikan bahwa saya menambahkan variabel fixed ke fungsi pow . Dalam hal ini, konteks eksekusi lokal akan berisi bagian untuk fixed . Saya tidak pandai menggambar persegi panjang kecil di dalam persegi kecil kecil lainnya, jadi gunakan imajinasi Anda.

Konteks eksekusi lokal akan muncul di sebelah pow , di dalam bagian persegi panjang hijau yang terletak di dalam konteks eksekusi global. Bayangkan juga bagaimana untuk setiap fungsi bersarang di dalam fungsi bersarang, mesin membuat konteks eksekusi lokal lainnya. Semua bagian persegi panjang ini muncul dengan sangat cepat! Seperti boneka bersarang!

Mari kita kembali ke kisah single-threaded. Apa artinya ini?

3. JavaScript adalah single-threaded, dan cerita menyenangkan lainnya


Kami mengatakan bahwa JavaScript adalah utas tunggal karena hanya satu tumpukan panggilan yang menangani fungsi kami . Biarkan saya mengingatkan Anda bahwa fungsi tidak dapat meninggalkan tumpukan panggilan jika fungsi lain mengharapkan eksekusi.

Ini bukan masalah jika kita bekerja dengan kode sinkron. Misalnya, penambahan dua angka adalah sinkron dan dihitung dalam mikrodetik. Bagaimana dengan panggilan jaringan dan interaksi lainnya dengan dunia luar?

Untungnya, mesin JavaScript dirancang untuk bekerja secara sinkron secara default . Bahkan jika mereka hanya dapat menjalankan satu fungsi pada satu waktu, fungsi yang lebih lambat dapat dilakukan oleh entitas eksternal - dalam kasus kami, ini adalah browser. Kami akan membicarakan ini di bawah ini.

Pada saat yang sama, Anda tahu bahwa ketika browser memuat beberapa jenis kode JavaScript, mesin membaca kode ini baris demi baris dan melakukan langkah-langkah berikut:

  • Menempatkan variabel dan deklarasi fungsi ke dalam memori global (heap).
  • Mengirim panggilan ke setiap fungsi di tumpukan panggilan.
  • Menciptakan konteks eksekusi global di mana fungsi global dieksekusi.
  • Membuat banyak konteks eksekusi lokal kecil (jika ada variabel internal atau fungsi bersarang).

Anda sekarang memiliki pemahaman dasar tentang mekanisme sinkronisasi yang mendasari semua mesin JavaScript. Pada bab selanjutnya, kita akan berbicara tentang cara kerja kode asinkron dalam JavaScript dan mengapa ia bekerja seperti itu.

4. Asynchronous JavaScript, antrian panggilan balik, dan loop acara


Berkat memori global, konteks eksekusi dan tumpukan panggilan, kode JavaScript sinkron dijalankan di browser kami. Tapi kami lupa sesuatu. Apa yang terjadi jika Anda perlu menjalankan semacam fungsi asinkron?

Dengan fungsi asinkron, maksud saya setiap interaksi dengan dunia luar, yang mungkin membutuhkan waktu untuk diselesaikan. Memanggil REST API atau timer tidak sinkron, karena dapat mengambil detik untuk menjalankannya. Berkat elemen yang tersedia di mesin, kami dapat memproses fungsi-fungsi tersebut tanpa memblokir tumpukan panggilan dan browser. Jangan lupa, tumpukan panggilan hanya dapat menjalankan satu fungsi pada satu waktu, dan bahkan satu fungsi pemblokiran dapat benar-benar menghentikan browser . Untungnya, mesin JavaScript cerdas, dan dengan sedikit bantuan dari browser, mereka dapat menyelesaikan masalah.

Saat kami menjalankan fungsi asinkron, browser mengambil dan menjalankannya untuk kami. Ambil timer seperti ini:

 setTimeout(callback, 10000); function callback(){ console.log('hello timer!'); } 

Saya yakin bahwa meskipun Anda telah melihat setTimeout ratusan kali, Anda mungkin tidak tahu bahwa fungsi ini tidak ada dalam JavaScript . Jadi, ketika JavaScript muncul, tidak ada fungsi setTimeout di dalamnya. Bahkan, itu adalah bagian dari apa yang disebut API peramban, kumpulan alat yang nyaman yang disediakan peramban kepada kami. Luar biasa! Tetapi apa artinya ini dalam praktik? Karena setTimeout merujuk ke API peramban, fungsi ini dijalankan oleh peramban itu sendiri (untuk sesaat muncul di tumpukan panggilan, tetapi segera dihapus dari sana).

Setelah 10 detik, browser mengambil fungsi panggilan balik yang kami lewati dan menempatkannya dalam antrian panggilan balik . Saat ini, dua bagian persegi panjang telah muncul di mesin JavaScript. Lihatlah kode ini:

 var num = 2; function pow(num) { return num * num; } pow(num); setTimeout(callback, 10000); function callback(){ console.log('hello timer!'); } 

Sekarang skema kami terlihat seperti ini:



setTimeout dijalankan di dalam konteks browser. Setelah 10 detik, penghitung waktu dimulai dan fungsi panggilan balik siap untuk dieksekusi. Tapi pertama-tama, harus melalui antrian panggilan balik. Ini adalah struktur data dalam bentuk antrian, dan, seperti namanya, adalah fungsi antrian yang dipesan.

Setiap fungsi asinkron harus melalui antrian panggilan balik sebelum masuk ke tumpukan panggilan. Tapi siapa yang mengirim fungsi selanjutnya? Ini membuat komponen yang disebut loop peristiwa .

Sejauh ini, loop acara hanya berurusan dengan satu hal: ia memeriksa apakah tumpukan panggilan kosong. Jika ada fungsi dalam antrian panggilan balik dan jika tumpukan panggilan gratis, maka sudah waktunya untuk mengirim panggilan balik ke tumpukan panggilan.

Setelah itu, fungsi tersebut dianggap dijalankan. Ini adalah skema umum untuk memproses kode asinkron dan sinkron dengan mesin JavaScript:



Katakanlah callback() siap untuk dieksekusi. Ketika pow() tumpukan panggilan dibebaskan dan loop acara mengirim callback() . Dan itu dia! Meskipun saya sedikit menyederhanakan hal-hal, jika Anda memahami diagram di atas, Anda dapat memahami semua JavaScript.

Ingat: API berbasis browser, antrean panggilan balik, dan loop acara adalah pilar JavaScript asinkron .

Dan jika Anda tertarik, Anda dapat menonton video penasaran “Apa-apaan lingkaran acara” oleh Philip Roberts. Ini adalah salah satu penjelasan terbaik untuk loop acara.

Tapi kami belum selesai dengan tema JavaScript asinkron. Dalam bab-bab berikut, kami akan mempertimbangkan janji-janji ES6.

5. Callback hell dan ES6 menjanjikan


Fungsi panggilan balik digunakan dalam JavaScript di mana-mana, baik dalam kode sinkron maupun asinkron. Pertimbangkan metode ini:

 function mapper(element){ return element * 2; } [1, 2, 3, 4, 5].map(mapper); 

mapper adalah fungsi callback yang dilewatkan di dalam map . Kode di atas sinkron. Sekarang pertimbangkan interval ini:

 function runMeEvery(){ console.log('Ran!'); } setInterval(runMeEvery, 5000); 

Kode ini asinkron, karena di dalam setInterval kami melewati panggilan balik runMeEvery. Callback digunakan di seluruh JavaScript, jadi selama bertahun-tahun kami memiliki masalah yang disebut "panggilan balik neraka" - "panggilan balik neraka".

Istilah panggilan balik neraka dalam JavaScript diterapkan ke "gaya" pemrograman di mana panggilan balik tertanam dalam panggilan balik lain yang tertanam dalam panggilan balik lainnya ... Karena sifatnya yang tidak sinkron, programmer JavaScript telah lama terperangkap dalam perangkap ini.

Sejujurnya, saya tidak pernah membuat piramida callback besar. Mungkin karena saya menghargai kode yang dapat dibaca dan selalu berusaha untuk berpegang pada prinsip-prinsipnya. Jika Anda menekan neraka panggilan balik, itu berarti bahwa fungsi Anda melakukan terlalu banyak.

Saya tidak akan berbicara secara rinci tentang panggilan balik neraka, jika Anda tertarik, kemudian pergi ke callbackhell.com , di mana masalah ini telah diselidiki secara rinci dan berbagai solusi telah diajukan. Dan kita akan berbicara tentang janji ES6 . Ini adalah add-on JavaScript yang dirancang untuk memecahkan masalah panggilan balik neraka. Tapi apa janji itu?

Janji JavaScript adalah representasi acara di masa mendatang . Janji dapat berakhir dengan sukses, atau dalam jargon programmer, janji akan "diselesaikan" (diselesaikan). Tetapi jika janji berakhir dengan kesalahan, maka kita mengatakan bahwa itu dalam keadaan ditolak. Janji juga memiliki status default: setiap janji baru dimulai dalam status tertunda. Bisakah saya membuat janji saya sendiri? Ya Kita akan membicarakan ini di bab selanjutnya.

6. Membuat dan bekerja dengan janji-janji JavaScript


Untuk membuat janji baru, Anda harus memanggil konstruktor dengan meneruskan fungsi panggilan balik ke sana. Ini hanya dapat mengambil dua parameter: resolve dan reject . Mari kita buat janji baru yang akan diselesaikan dalam 5 detik (Anda dapat menguji contoh di konsol browser):

 const myPromise = new Promise(function(resolve){ setTimeout(function(){ resolve() }, 5000) }); 

Seperti yang Anda lihat, resolve adalah fungsi yang kami panggil sehingga janji berakhir dengan sukses. Dan reject akan membuat janji yang ditolak:

 const myPromise = new Promise(function(resolve, reject){ setTimeout(function(){ reject() }, 5000) }); 

Perhatikan bahwa Anda dapat mengabaikan reject karena ini adalah parameter kedua. Tetapi jika Anda ingin menggunakan reject , Anda tidak bisa mengabaikan resolve . Artinya, kode berikut tidak akan berfungsi dan akan berakhir dengan janji yang diizinkan:

 // Can't omit resolve ! const myPromise = new Promise(function(reject){ setTimeout(function(){ reject() }, 5000) }); 

Janji tidak terlihat sangat berguna saat ini, kan? Contoh-contoh ini tidak menampilkan apa pun kepada pengguna. Mari kita tambahkan sesuatu. Dan diizinkan, janji yang ditolak dapat mengembalikan data. Sebagai contoh:

 const myPromise = new Promise(function(resolve) { resolve([{ name: "Chris" }]); }); 

Tapi kami masih belum melihat apa-apa. Untuk mengekstrak data dari janji, Anda harus mengaitkan janji dengan metode itu . Dia menerima panggilan balik (betapa ironi!), Yang menerima data saat ini:

 const myPromise = new Promise(function(resolve, reject) { resolve([{ name: "Chris" }]); }); myPromise.then(function(data) { console.log(data); }); 

Sebagai pengembang JavaScript dan konsumen kode orang lain, Anda sebagian besar berinteraksi dengan janji-janji eksternal. Pembuat perpustakaan paling sering membungkus kode warisan dalam konstruktor Promise, seperti ini:

 const shinyNewUtil = new Promise(function(resolve, reject) { // do stuff and resolve // or reject }); 

Dan jika perlu, kami juga dapat membuat dan menyelesaikan janji dengan menelepon Promise.resolve() :

 Promise.resolve({ msg: 'Resolve!'}) .then(msg => console.log(msg)); 

Jadi, izinkan saya mengingatkan Anda: Janji JavaScript adalah penanda untuk acara yang akan terjadi di masa depan. Suatu peristiwa dimulai dalam keadaan "menunggu keputusan", dan dapat berhasil (diizinkan, dieksekusi) atau tidak berhasil (ditolak). Sebuah janji dapat mengembalikan data yang dapat diambil dengan melampirkan then . Dalam bab selanjutnya, kita akan membahas cara mengatasi kesalahan yang datang dari janji.

7. Penanganan Kesalahan dalam Janji ES6


Menangani kesalahan dalam JavaScript selalu mudah, setidaknya dalam kode sinkron. Lihatlah sebuah contoh:

 function makeAnError() { throw Error("Sorry mate!"); } try { makeAnError(); } catch (error) { console.log("Catching the error! " + error); } 

Hasilnya adalah:

 Catching the error! Error: Sorry mate! 

Seperti yang diharapkan, kesalahan jatuh ke catch . Sekarang coba fungsi asinkron:

 function makeAnError() { throw Error("Sorry mate!"); } try { setTimeout(makeAnError, 5000); } catch (error) { console.log("Catching the error! " + error); } 

Kode ini asinkron karena setTimeout . Apa yang akan terjadi jika kita menjalankannya?

  throw Error("Sorry mate!"); ^ Error: Sorry mate! at Timeout.makeAnError [as _onTimeout] (/home/valentino/Code/piccolo-javascript/async.js:2:9) 

Sekarang hasilnya berbeda. Kesalahan tidak ditangkap oleh catch , tetapi bebas naik tumpukan. Alasannya adalah bahwa try/catch hanya berfungsi dengan kode sinkron. Jika Anda ingin tahu lebih banyak, maka masalah ini dibahas secara rinci di sini .

Untungnya, dengan janji, kita dapat menangani kesalahan asinkron seolah-olah itu sinkron. Dalam bab terakhir, saya mengatakan bahwa panggilan reject menyebabkan penolakan terhadap janji:

 const myPromise = new Promise(function(resolve, reject) { reject('Errored, sorry!'); }); 

Dalam hal ini, kita dapat menangani kesalahan menggunakan catch handler dengan menarik (lagi) panggilan balik:

 const myPromise = new Promise(function(resolve, reject) { reject('Errored, sorry!'); }); myPromise.catch(err => console.log(err)); 

Selain itu, untuk membuat dan menolak janji di tempat yang tepat, Anda dapat menghubungi Promise.reject() :

 Promise.reject({msg: 'Rejected!'}).catch(err => console.log(err)); 

Biarkan saya mengingatkan Anda: pawang then dieksekusi ketika janji dieksekusi, dan pawang catch dieksekusi karena janji yang ditolak. Tapi ini bukan akhir dari cerita. Di bawah ini kita akan melihat bagaimana async/await berfungsi dengan baik dengan try/catch .

8. Combinators dari ES6 berjanji: Promise.all, Promise.allSettled, Promise.any dan lainnya


Janji tidak dirancang untuk bekerja sendiri. API Janji menawarkan sejumlah metode untuk menggabungkan janji . Salah satu yang paling berguna adalah Promise.all , dibutuhkan array dari janji dan mengembalikan satu janji. Satu-satunya masalah adalah bahwa Janji. Semua ditolak jika setidaknya satu janji dalam array ditolak.

Promise.race mengizinkan atau menolak segera setelah salah satu janji dalam array menerima status yang sesuai.

Dalam versi V8 yang lebih baru, dua kombinator baru juga akan diperkenalkan: Promise.allSettled dan Promise.any . Promise.any masih pada tahap awal dari fungsionalitas yang diusulkan, pada saat penulisan artikel ini tidak didukung. Namun, secara teori, ia akan dapat memberi sinyal apakah janji telah dieksekusi. Perbedaan dari Promise.race adalah bahwa Promise.any tidak ditolak, bahkan jika salah satu dari janji itu ditolak .

Promise.allSettled bahkan lebih menarik. Dia juga menerima sejumlah janji, tetapi tidak “mempersingkat” jika salah satu dari janji itu ditolak. Ini berguna ketika Anda perlu memeriksa apakah semua janji dalam array telah melewati beberapa tahap, terlepas dari adanya janji yang ditolak. Itu bisa dianggap kebalikan dari Promise.all .

9. ES6 Janji dan antrian mikrotask


Jika Anda ingat dari bab sebelumnya, setiap fungsi panggilan balik tidak sinkron dalam JavaScript ada dalam antrean panggilan balik sebelum menyentuh tumpukan panggilan. Tetapi fungsi callback yang diteruskan ke janji memiliki nasib yang berbeda: mereka diproses oleh antrian mikrotask daripada antrian tugas.

Dan di sini Anda perlu berhati-hati: antrian mikrotask mendahului antrian panggilan . Callback dari antrian mikrotask diutamakan ketika loop acara memeriksa untuk melihat apakah callback baru siap untuk pergi pada tumpukan panggilan.

Mekanika ini dijelaskan secara lebih rinci oleh Jake Archibald dalam Tugas, mikrotasks, antrian dan jadwal , bacaan yang bagus.

10. Mesin JavaScript: bagaimana cara kerjanya? Evolusi asinkron: dari janji menjadi asinkron / menunggu


JavaScript berkembang dengan cepat dan kami terus mendapatkan peningkatan setiap tahun. Janji tampak seperti penutup, tetapi dengan ECMAScript 2017 (ES8) muncul sintaks baru: async/await .

async/await hanya perbaikan gaya yang kita sebut gula sintaksis. async/await tidak mengubah JavaScript dengan cara apa pun (jangan lupa bahwa bahasa tersebut harus kompatibel dengan peramban yang lebih lama dan tidak boleh melanggar kode yang ada). Ini hanya cara baru untuk menulis kode asinkron berdasarkan janji. Pertimbangkan sebuah contoh. Di atas, kami sudah menyimpan janji di korespondensi then :

 const myPromise = new Promise(function(resolve, reject) { resolve([{ name: "Chris" }]); }); myPromise.then((data) => console.log(data)) 

Sekarang dengan async/await kita dapat memproses kode asinkron sehingga bagi pembaca daftar kita kodenya terlihat sinkron . Alih-alih menggunakan then kita bisa membungkus janji dalam fungsi berlabel async , dan kemudian kita akan await hasilnya:

 const myPromise = new Promise(function(resolve, reject) { resolve([{ name: "Chris" }]); }); async function getData() { const data = await myPromise; console.log(data); } getData(); 

Terlihat bagus, bukan? Lucu bahwa fungsi async selalu mengembalikan janji, dan tidak ada yang bisa menghentikannya dari melakukan ini:

 async function getData() { const data = await myPromise; return data; } getData().then(data => console.log(data)); 

Bagaimana dengan kesalahan? Salah satu keuntungan dari async/await adalah konstruksi ini memungkinkan kita untuk menggunakan try/catch . Baca pengantar penanganan kesalahan dalam fungsi async dan pengujiannya .

Mari kita lihat janji itu lagi, di mana kita menangani kesalahan dengan pengatur catch :

 const myPromise = new Promise(function(resolve, reject) { reject('Errored, sorry!'); }); myPromise.catch(err => console.log(err)); 

Dengan fungsi asinkron, kita dapat melakukan refactor seperti ini:

 async function getData() { try { const data = await myPromise; console.log(data); // or return the data with return data } catch (error) { console.log(error); } } getData(); 

Namun, tidak semua orang beralih ke gaya ini. try/catch dapat menyulitkan kode Anda. Ada satu hal lagi yang perlu dipertimbangkan. Lihat bagaimana kesalahan terjadi di dalam blok try ini dalam kode ini:

 async function getData() { try { if (true) { throw Error("Catch me if you can"); } } catch (err) { console.log(err.message); } } getData() .then(() => console.log("I will run no matter what!")) .catch(() => console.log("Catching err")); 

Bagaimana dengan dua garis yang ditampilkan di konsol? Ingatlah bahwa try/catch adalah konstruk sinkron, dan fungsi asinkron kami menghasilkan janji . Mereka mengikuti dua jalur yang berbeda, seperti kereta. ! , throw , catch getData() . , «Catch me if you can», «I will run no matter what!».

, throw then . , , Promise.reject() :

 async function getData() { try { if (true) { return Promise.reject("Catch me if you can"); } } catch (err) { console.log(err.message); } } Now the error will be handled as expected: getData() .then(() => console.log("I will NOT run no matter what!")) .catch(() => console.log("Catching err")); "Catching err" // output 

async/await JavaScript. .

, JS- async/await . . , async/await — .

11. JavaScript-: ?


JavaScript — , , . JS-: V8, Google Chrome Node.js; SpiderMonkey, Firefox; JavaScriptCore, Safari.

JavaScript- «» : , , , . , .

JavaScript- , . JavaScript: , - , (, ) .

ECMAScript 2015 . — , . . 2017- async/await : , , .

Source: https://habr.com/ru/post/id452906/


All Articles