Async JS Di Balik Terpal

Jan 27 2023
MDN mendefinisikan alur kerja asinkron sebagai ..

MDN mendefinisikan alur kerja asinkron sebagai

..memulai tugas yang berpotensi berjalan lama dan masih bisa responsif terhadap acara lain saat tugas itu berjalan… Setelah tugas itu selesai, program Anda disajikan dengan hasilnya.

Tapi bagaimana tepatnya ini bisa dicapai di web dengan JavaScript?

Sebelum terjun ke implementasi async, ada baiknya memiliki pemahaman dasar tentang JS di tingkat yang lebih rendah.

Bagaimana cara kerja JS?

JavaScript adalah single-threaded dan ditafsirkan, artinya instruksi dieksekusi secara langsung dan tidak memiliki langkah kompilasi ex-ante. Mari kita ambil V8, mesin paling populer sebagai contoh.

Pada level tinggi, V8 mem-parsing kode, membuat pohon sintaksis abstrak (AST) , dan mengompilasinya menjadi bytecode yang dioptimalkan dengan juru bahasa. Ada juga preparser, dua kompiler, dan caching yang membuatnya cepat (V8 memiliki blog yang bagus tentang ini). Itu hanya memberi kita tumpukan panggilan, tumpukan, dan loop peristiwa ( semacam, tidak juga) . Kita dapat mengabaikan heap untuk keperluan lubang kelinci ini: ini adalah potongan memori tidak terstruktur dengan referensi objek dan variabel.

Putaran Acara Runtime

Tidak seperti Java atau C++, JS memungkinkan eksekusi satu peristiwa dalam satu waktu. Kami dapat melihat ini di browser kami dengan melacak jalur panggilan dengan console.trace():

// shoutout to ES6 arrow functions

foo = () => {
    console.log("Hello, World!");
    bar();
}
bar = () => {
    console.log("Goodbye, World!");
    trace();
}

trace = () => console.trace();

foo();

Hello, World!   VM1049:6
Goodbye, World! VM1049:11 

// console.trace
trace @ VM1049:11
bar @ VM1049:7
foo @ VM1049:3

      
                
call stack visualization

Set Timeout

Mari jalankan metode yang sama dengan penundaan asinkron 5000 ms dengan setTimeout().

foo = () => {
    console.log("Hello, World!");
     setTimeout(bar, 5000);
}

bar = () => {
    console.log("Goodbye, World!");
    trace();
}

trace = () => console.trace();

foo();

Hello, World!   VM1526:2

// 3 days later... 
Goodbye, World!  VM1526:6

// console.trace 
trace @ VM1382:10
bar @ VM1382:6

// setTimeout is invoked
b @ main-base.bundle.UM3…oFn_e5gQ.12.js:1723
setTimeout (async)  
Cga.window.setTimeout @ main-base.bundle.UM3…toFn_e5gQ.12.js:516

// foo is pushed to the stack first
foo @ VM1382:3

setTimeoutbukan bagian dari mesin JS, yang masuk akal karena mesin dapat menangani satu peristiwa dalam satu waktu. Browser , bagaimanapun, adalah multi-utas dan memberi kami API web untuk mengakses implementasi setTimeout. Misalnya, Chrome menggunakan mesin V8 single-threaded dalam arsitektur multi-proses, di mana setiap proses sangat multi-threaded. Dalam hal ini, ini secara eksplisit dipanggil dengan Cga.setTimeout @ main-base.bundle.blah-blah-blah...Virtually, ini memungkinkan kami untuk menggunakan banyak utas.

Putaran Acara

Mari menyusun ulang blok kode kita:

foo = () => {
    console.log("Hello, World!");
     setTimeout(bar, 5000);
}

bar = () => {
    setTimeout(() => console.log("Goodbye, World!"), 5000);
    trace();
}

trace = () => console.trace();

foo();

// output of foo 
Hello, World! VM1649:11 

// console.trace 
trace @ VM1694:11
bar @ VM1694:8
b @ main-base.bundle.UM3…oFn_e5gQ.12.js:1723
setTimeout (async)  
Cga.window.setTimeout @ main-base.bundle.UM3…toFn_e5gQ.12.js:516
foo @ VM1694:3
// end trace 

// 3 days later...
VM1649:6 Goodbye, World!

JRE meliputi:

  • JS Engine (berulir tunggal)
  • Java Virtual Machine (JVM) — Mesin virtual yang mengeksekusi kode saat runtime. Omong-omong, inilah mengapa "VM" menambahkan log pelacakan.
  • API web
  • Antrean tugas
  • Putaran acara

Tumpukan panggilan mesin kami membuka acara satu per satu. Setelah melihat setTimeout(), metode, bersama dengan callback (referensi fungsi), didelegasikan ke web API.

Perhatikan bahwa mesin tidak lagi peduli bagaimana implementasi setTimeout API menangani event dan callback. Mesin bergerak untuk memunculkan acara berikutnya dan memasukkannya ke Pacman (JVM yang mengeksekusi kode).

Sementara itu, API menggunakan utasnya untuk menunggu 5000 md dan selanjutnya menyerahkannya ke antrean tugas. Perulangan peristiwa melihat bahwa peristiwa sejalan dan mendorongnya kembali ke tumpukan panggilan jika dan hanya jika tumpukan panggilan kosong.

Anda dapat menggunakan visualisator menyenangkan Philip Roberts untuk melihat semuanya beraksi. Juga, lihat pembicaraannya yang luar biasa di JSConf pada tahun 2014.