
Cara cepat memulai VUE3.0: Pelajari
tentang konteks eksekusi, tumpukan eksekusi, dan mekanisme eksekusi (tugas sinkron, tugas asinkron, tugas mikro, tugas makro, dan loop peristiwa) di js Beberapa teman-teman mungkin merasa bingung ketika ditanya, maka dari itu saya akan merangkumnya hari ini, semoga dapat membantu anda di depan layar.
Sebelum membahas konteks eksekusi dan mekanisme eksekusi js di js , mari kita bahas thread dan proses.
Secara resmi,线程adalah unit terkecil dari penjadwalan CPU .
? Secara resmi,进程adalah unit terkecil dari alokasi sumber daya CPU .
线程线程unit yang menjalankan program berdasarkan进程. Dalam istilah进程,线程adalah alur eksekusi dalam suatu program.
Hanya ada satu alur eksekusi dalam suatu进程yang disebut单线程Artinya, ketika program dijalankan, jalur program yang diambil disusun secara berurutan.
Beberapa aliran eksekusi dalam suatu进程disebut多线程, yaitu, beberapa线程berbeda dapat dijalankan secara bersamaan dalam suatu program untuk melakukan tugas yang berbeda, yang berarti bahwa satu program diperbolehkan membuat beberapa线程eksekusi paralel untuk menyelesaikan tugasnya masing-masing. .
Penulis akan memberikan contoh sederhana di bawah ini, misalnya jika kita membuka qq音乐dan mendengarkan lagu, qq音乐qq音乐dipahami sebagai sebuah proses ke lagu adalah sebuah thread, dan mengunduh adalah sebuah proses. Jika kita membuka vscode lagi untuk menulis kode, itu akan menjadi proses lain.
Prosesnya independen satu sama lain, namun beberapa sumber daya dibagi antar thread dalam proses yang sama.
Siklus hidup sebuah thread melewati lima tahap.
Status baru: Setelah menggunakan kata kunci new dan kelas Thread atau subkelasnya untuk membuat objek thread, objek thread berada dalam status baru. Tetap dalam keadaan ini sampai program start() thread.
Status siap: Ketika objek thread memanggil metode start() , thread memasuki status siap. Thread dalam keadaan siap berada dalam antrian siap dan dapat segera dijalankan selama ia memperoleh hak untuk menggunakan CPU .
Status berjalan: Jika thread dalam status siap memperoleh sumber daya CPU , thread dapat mengeksekusi run() , dan thread berada dalam status berjalan. Thread dalam keadaan berjalan adalah yang paling rumit, dapat diblokir, siap dan mati.
Status pemblokiran: Jika thread menjalankan metode sleep(睡眠) , suspend(挂起) , wait(等待) dan lainnya, setelah kehilangan sumber daya yang ditempati, thread akan memasuki status pemblokiran dari status berjalan. Status siap dapat dimasukkan kembali setelah waktu tidur habis atau sumber daya perangkat telah diperoleh. Ini dapat dibagi menjadi tiga jenis:
pemblokiran menunggu: thread dalam keadaan berjalan mengeksekusi metode wait() , menyebabkan thread memasuki keadaan pemblokiran menunggu.
Pemblokiran sinkron: Thread gagal memperoleh kunci sinkronisasi synchronized (karena kunci sinkronisasi ditempati oleh thread lain).
Pemblokiran lainnya: Ketika permintaan I/O dikeluarkan dengan memanggil sleep() atau join() thread, thread akan memasuki status pemblokiran. Ketika status sleep() habis waktu, join() menunggu thread berakhir atau waktu habis, atau pemrosesan I/O selesai, dan thread kembali ke status siap.
Status kematian: Ketika thread yang sedang berjalan menyelesaikan tugasnya atau kondisi terminasi lainnya terjadi, thread beralih ke status dihentikan.

JS
Sebagai bahasa skrip browser, JS terutama digunakan untuk berinteraksi dengan pengguna dan mengoperasikan DOM . Ini menentukan bahwa itu hanya dapat berupa single-thread, jika tidak maka akan menyebabkan masalah sinkronisasi yang sangat kompleks. Misalnya, JavaScript memiliki dua thread secara bersamaan. Satu thread menambahkan konten ke node DOM tertentu, dan thread lainnya menghapus node tersebut.
Ketika mesin JS mem-parsing fragmen kode yang dapat dieksekusi (biasanya fase pemanggilan fungsi), ia akan melakukan beberapa pekerjaan persiapan terlebih dahulu sebelum eksekusi. (Konteks eksekusi (disebut EC )" atau bisa juga disebut lingkungan eksekusi .
Ada tiga jenis konteks eksekusi dalam javascript , yaitu:
konteks eksekusi global . Ini adalah konteks eksekusi default atau paling dasar. Hanya akan ada satu konteks global dalam suatu program, dan itu akan ada sepanjang siklus hidup program Skrip javascript . Bagian bawah tumpukan eksekusi tidak akan dihancurkan oleh tumpukan yang muncul. Konteks global akan menghasilkan objek global (mengambil lingkungan browser sebagai contoh, objek global ini adalah window ), dan mengikat nilai this ke objek global ini.
Konteks Eksekusi Fungsi Setiap kali suatu fungsi dipanggil, konteks eksekusi fungsi baru dibuat (terlepas dari apakah fungsi tersebut dipanggil berulang kali).
Konteks eksekusi fungsi eval Kode yang dieksekusi di dalam fungsi eval juga akan memiliki konteks eksekusinya sendiri, tetapi karena eval tidak sering digunakan, kode tersebut tidak akan dianalisis di sini.
Tadi sudah kita sebutkan bahwa js akan membuat konteks eksekusi saat dijalankan, namun konteks eksekusi tersebut perlu disimpan, lalu apa yang digunakan untuk menyimpannya? Anda perlu menggunakan struktur data tumpukan.
Tumpukan adalah struktur data yang pertama masuk terakhir keluar.

Jadi ringkasnya, konteks eksekusi yang digunakan untuk menyimpan konteks eksekusi yang dibuat saat kode dijalankan adalah tumpukan eksekusi .
Saat mengeksekusi sepotong kode JS
Kemudian mesin JS akan membuat konteks eksekusi global dan push ke tumpukan eksekusi. Dalam proses ini, mesin JS akan mengalokasikan memori untuk semua variabel dalam kode ini dan menetapkan nilai awal (tidak ditentukan). Mesin JS akan memasuki tahap eksekusi, dalam proses ini mesin JS akan mengeksekusi kode baris demi baris, yaitu memberikan nilai (nilai sebenarnya) ke variabel yang telah dialokasikan memorinya satu per satu.
Jika ada pemanggilan function dalam kode ini, mesin JS akan membuat konteks eksekusi fungsi dan push ke tumpukan eksekusi. Proses pembuatan dan eksekusi sama dengan konteks eksekusi global.
Ketika tumpukan eksekusi selesai, konteks eksekusi akan dikeluarkan dari tumpukan, dan kemudian konteks eksekusi berikutnya akan dimasukkan.
Izinkan saya memberikan contoh di bawah ini. Jika kita memiliki kode berikut dalam program kita:
console.log("Global Execution Context start");
fungsi pertama() {
console.log("fungsi pertama");
Kedua();
console.log("Lagi fungsi pertama");
}
fungsi kedua() {
console.log("fungsi kedua");
}
Pertama();
console.log("Konteks Eksekusi Global berakhir"); Mari kita analisis secara singkat contoh di atas
Pertama, tumpukan eksekusi akan dibuat
, kemudian konteks global akan dibuat, dan konteks eksekusi akan push ke tumpukan eksekusi
untuk memulai eksekusi. , dan Global Execution Context start
temui metode first , jalankan metode, buat konteks eksekusi fungsi dan push ke tumpukan eksekusi
untuk mengeksekusi konteks eksekusi first , keluaran first function
temui metode second , jalankan metode. , membuat konteks eksekusi fungsi dan push ke tumpukan eksekusi untuk
mengeksekusi Konteks eksekusi second , menampilkan second function
Konteks eksekusi second telah dieksekusi, dikeluarkan dari tumpukan, dan memasuki konteks first
first eksekusi, keluaran Again first function
Konteks eksekusi first telah dieksekusi, dikeluarkan dari tumpukan, dan memasuki konteks Eksekusi berikutnya Konteks eksekusi global Konteks eksekusi
global Lanjutkan eksekusi dan keluaran Global Execution Context end
Kami menggunakan gambar untuk meringkas

Baiklah. Setelah membahas konteks eksekusi dan tumpukan eksekusi, mari kita bicara tentang mekanisme eksekusi js. Berbicara tentang mekanisme eksekusi js
js perlu memahami tugas sinkron, tugas asinkron, tugas makro, dan tugas mikro di js .
Di js , tugas dibagi menjadi tugas sinkron dan tugas asinkron.
Tugas sinkron mengacu pada tugas yang diantri untuk dieksekusi di thread utama. Tugas berikutnya hanya dapat dijalankan setelah tugas sebelumnya dijalankan.
Tugas asinkron mengacu pada tugas yang tidak masuk ke thread utama tetapi masuk ke "antrian tugas" (tugas dalam antrian tugas dijalankan secara paralel dengan thread utama hanya ketika thread utama tidak aktif dan "antrian tugas" memberi tahu thread utama, tugas asinkron Setelah dapat dijalankan, tugas tersebut akan memasuki thread utama untuk dieksekusi. Karena merupakan penyimpanan antrean, maka memenuhi aturan masuk pertama keluar pertama . Tugas asinkron yang umum mencakup setInterval , setTimeout , promise.then , dll.
sebelumnya telah memperkenalkan tugas sinkron dan tugas asinkron. Sekarang mari kita bicara tentang perulangan peristiwa.
Tugas sinkron dan asinkron masing-masing memasuki "tempat" eksekusi yang berbeda, dan memasuki thread utama secara sinkron. Hanya ketika tugas sebelumnya selesai, tugas berikutnya dapat dijalankan. Tugas asinkron tidak masuk ke thread utama tetapi masuk ke Event Table dan fungsi register.
Ketika hal yang ditentukan sudah selesai, Event Table akan memindahkan fungsi ini ke dalam Event Queue . Event Queue adalah struktur data antrian, sehingga memenuhi aturan masuk pertama keluar pertama.
Ketika tugas di thread utama kosong setelah eksekusi, fungsi terkait akan dibaca dari Event Queue dan dieksekusi di thread utama.
Proses diatas akan berulang secara terus menerus yang sering disebut dengan Event Loop .
Mari kita rangkum dengan sebuah gambar

Izinkan saya memperkenalkan secara singkat sebuah contoh
fungsi test1() {
konsol.log("log1");
setWaktu habis(() => {
console.log("setTimeout 1000");
}, 1000);
setWaktu habis(() => {
console.log("setTimeout 100");
}, 100);
konsol.log("log2");
}
test1(); // log1, log2, setTimeout 100, setTimeout 1000 Kita tahu bahwa di js, tugas sinkron akan dieksekusi terlebih dahulu sebelum tugas asinkron, jadi contoh di atas akan menampilkan log1、log2 terlebih dahulu,
lalu menjalankan tugas asinkron setelah sinkron
itu
, fungsi panggilan balik dengan penundaan 100 milidetik akan mengeksekusi keluaran setTimeout 100
1000 setTimeout 1000
selama Anda memahami tugas sinkron dan asinkron yang disebutkan penulis di atas, tidak akan ada masalah. Kalau begitu izinkan saya memberi Anda contoh lain. Teman-teman, mari kita lihat seperti apa hasilnya.
fungsi tes2() {
konsol.log("log1");
setWaktu habis(() => {
console.log("setTimeout 1000");
}, 1000);
setWaktu habis(() => {
console.log("setTimeout 100");
}, 100);
Janji baru((putuskan, tolak) => {
console.log("janji baru");
menyelesaikan();
}).lalu(() => {
console.log("janji.lalu");
});
konsol.log("log2");
}
test2(); Untuk mengatasi masalah di atas, mengetahui tugas sinkron dan asinkron saja tidak cukup. Kita juga perlu mengetahui tugas makro dan tugas mikro.
Di js , tugas dibagi menjadi dua jenis, satu disebut tugas makro MacroTask , dan yang lainnya disebut tugas mikro MicroTask .
Tugas makro umum MacroTask memiliki
blok kode utama
setTimeout()
setInterval()
setImmediate() - Node
requestAnimationFrame() - browser.
Tugas mikro umum MicroTask memiliki
Promise.then()
process.nextTick() -
Jadi di contoh di atas Ini melibatkan tugas makro dan tugas mikro. Bagaimana urutan pelaksanaan tugas makro dan tugas mikro?
Pertama-tama, ketika keseluruhan script (sebagai tugas makro pertama) mulai dijalankan, semua kode akan dibagi menjadi dua bagian: tugas sinkron dan tugas asinkron akan langsung memasuki thread utama untuk dieksekusi secara berurutan, dan tugas-tugas asinkron akan memasuki antrian asinkron dan kemudian Dibagi menjadi tugas makro dan tugas mikro.
Tugas makro memasuki Event Table dan mendaftarkan fungsi panggilan balik di dalamnya. Setiap kali acara yang ditentukan selesai, Event Table akan memindahkan Event Queue ini
ke Antrean Acara. Tugas mikro juga akan memasukkan Event Table lain dan mendaftar di dalamnya .Setiap kali acara yang ditentukan selesai, Event Table akan memindahkan fungsi ini ke Event Queue
Ketika tugas di thread utama selesai dan thread utama kosong, Event Queue dari tugas mikro akan diperiksa , semua Jalankan, jika tidak, jalankan tugas makro berikutnya.
Kami menggunakan gambar untuk meringkasnya.

Setelah memahami contoh tugas makro dan tugas mikro secara asinkron di atas, kita dapat dengan mudah mendapatkan jawabannya.
Kita tahu bahwa di js, tugas sinkron akan dieksekusi terlebih dahulu sebelum tugas asinkron, jadi contoh di atas akan menampilkan log1、new promise、log2 terlebih dahulu. Perlu dicatat di sini bahwa blok kode utama dari janji baru disinkronkan.
Setelah tugas makro dijalankan, semua tugas mikro yang dihasilkan oleh tugas makro ini akan dijalankan, jadi promise.then akan ditampilkan
setelah semua tugas mikro dijalankan , tugas makro lain akan dieksekusi, menunda fungsi panggilan balik 100 milidetik akan memprioritaskan eksekusi dan keluaran setTimeout 100
Tugas makro ini tidak menghasilkan tugas mikro, jadi tidak ada tugas mikro yang perlu dijalankan
untuk terus menjalankan tugas makro berikutnya fungsi dengan penundaan 1000 akan memprioritaskan eksekusi dan output setTimeout 1000
jadi setelah metode test2 dijalankan, itu akan Output log1、new promise、log2、promise.then、setTimeout 100、setTimeout 1000 secara
berurutan Ada perbedaan pendapat tentang apakah akan menjalankan
jsterlebih dahulu dengan tugas makro dan kemudian tugas mikro atau dengan tugas mikro sebelum tugas makro. Pemahaman penulis adalah jika seluruh blok kodejsdianggap sebagai tugas makro, urutan eksekusijskita adalah tugas makro terlebih dahulu dan kemudian tugas mikro.
Seperti kata pepatah, berlatih sekali lebih baik daripada menonton seratus kali di bawah ini. Jika Anda bisa melakukannya dengan benar, berarti Anda sudah menguasai ilmu mekanisme eksekusi js .
Contoh 1
fungsi test3() {
konsol.log(1);
setTimeout(fungsi () {
konsol.log(2);
Janji baru(fungsi (putuskan) {
konsol.log(3);
menyelesaikan();
}).lalu(fungsi () {
konsol.log(4);
});
konsol.log(5);
}, 1000);
Janji baru(fungsi (putuskan) {
konsol.log(6);
menyelesaikan();
}).lalu(fungsi () {
konsol.log(7);
setTimeout(fungsi () {
konsol.log(8);
});
});
setTimeout(fungsi () {
konsol.log(9);
Janji baru(fungsi (putuskan) {
konsol.log(10);
menyelesaikan();
}).lalu(fungsi () {
konsol.log(11);
});
}, 100);
konsol.log(12);
}
test3(); Mari kita analisis secara detail.
Pertama, keseluruhan blok kode js dijalankan sebagai tugas makro, dan 1, 1、6、12 dikeluarkan secara berurutan.
Setelah keseluruhan tugas makro blok kode dijalankan, satu tugas mikro dan dua tugas makro dihasilkan, sehingga antrian tugas makro memiliki dua tugas makro dan antrian tugas mikro memiliki satu tugas mikro.
Setelah tugas makro dijalankan, semua tugas mikro yang dihasilkan oleh tugas makro ini akan dijalankan. Karena hanya ada satu microtask, maka akan dihasilkan 7 . Tugas mikro ini memunculkan tugas makro lainnya, jadi saat ini terdapat tiga tugas makro dalam antrean tugas makro.
Di antara ketiga tugas makro, tugas makro yang tidak disetel akan dieksekusi terlebih dahulu, jadi tugas makro ini adalah keluaran 8 Tugas makro ini tidak menghasilkan tugas mikro, sehingga tidak ada tugas mikro yang harus dijalankan, dan tugas makro berikutnya terus dijalankan.
Tunda eksekusi makrotask selama 100 milidetik, keluaran 9、10 , dan hasilkan tugas mikro, sehingga antrian tugas mikro saat ini memiliki tugas mikro.
Setelah tugas makro dijalankan, semua tugas mikro yang dihasilkan oleh tugas makro akan dijalankan, sehingga tugas mikro akan dieksekusi
dieksekusi
. Semua tugas mikro dalam keluaran antrian tugas 11
Eksekusi tugas makro menghasilkan 2、3、5 dengan penundaan 1000 milidetik, dan tugas mikro dihasilkan. Oleh karena itu, antrian tugas mikro saat ini memiliki tugas mikro.
makrotask akan dijalankan. Semua tugas mikro dihasilkan, sehingga semua tugas mikro dalam antrian tugas mikro akan dieksekusi, dan 4 akan menjadi keluaran
. Jadi contoh kode di atas akan menampilkan 1、6、12、7、8、9、10、11、2、3、5、4 , 12, 7, 8, 9, 10, 11. , 2, 3, 5, 4 secara berurutan.
Contoh 2:
Kami sedikit memodifikasi contoh 1 di atas async await
fungsi async dan menunggu async test4() {
konsol.log(1);
setTimeout(fungsi () {
konsol.log(2);
Janji baru(fungsi (putuskan) {
konsol.log(3);
menyelesaikan();
}).lalu(fungsi () {
konsol.log(4);
});
konsol.log(5);
}, 1000);
Janji baru(fungsi (putuskan) {
konsol.log(6);
menyelesaikan();
}).lalu(fungsi () {
konsol.log(7);
setTimeout(fungsi () {
konsol.log(8);
});
});
hasil const = menunggu async1();
console.log(hasil);
setTimeout(fungsi () {
konsol.log(9);
Janji baru(fungsi (putuskan) {
konsol.log(10);
menyelesaikan();
}).lalu(fungsi () {
konsol.log(11);
});
}, 100);
konsol.log(12);
}
fungsi asinkron async1() {
konsol.log(13)
return Promise.resolve("Janji.resolve");
}
test4(); Apa yang akan dihasilkan oleh contoh di atas? Di sini kita dapat dengan mudah menyelesaikan masalah async dan await .
Kita tahu async dan await sebenarnya adalah gula sintaksis untuk Promise . Di sini kita hanya perlu mengetahui await setara dengan Promise.then . Jadi kita bisa memahami contoh di atas seperti kode berikut
function test4() {
konsol.log(1);
setTimeout(fungsi () {
konsol.log(2);
Janji baru(fungsi (putuskan) {
konsol.log(3);
menyelesaikan();
}).lalu(fungsi () {
konsol.log(4);
});
konsol.log(5);
}, 1000);
Janji baru(fungsi (putuskan) {
konsol.log(6);
menyelesaikan();
}).lalu(fungsi () {
konsol.log(7);
setTimeout(fungsi () {
konsol.log(8);
});
});
Janji baru(fungsi (putuskan) {
konsol.log(13);
return resolve("Janji.resolve");
}).lalu((hasil) => {
console.log(hasil);
setTimeout(fungsi () {
konsol.log(9);
Janji baru(fungsi (putuskan) {
konsol.log(10);
menyelesaikan();
}).lalu(fungsi () {
konsol.log(11);
});
}, 100);
konsol.log(12);
});
}
test4(); Bisakah Anda dengan mudah mendapatkan hasilnya setelah melihat kode di atas?
Pertama, seluruh blok kode js awalnya dijalankan sebagai tugas makro, dan menghasilkan keluaran 1、6、13 secara berurutan.
Setelah keseluruhan tugas makro blok kode dijalankan, dua tugas mikro dan satu tugas makro dihasilkan, sehingga antrian tugas makro memiliki satu tugas makro dan antrian tugas mikro memiliki dua tugas mikro.
Setelah tugas makro dijalankan, semua tugas mikro yang dihasilkan oleh tugas makro ini akan dijalankan. Jadi 7、Promise.resolve、12 akan menjadi output. Tugas mikro ini memunculkan dua tugas makro lagi, sehingga antrean tugas makro saat ini memiliki tiga tugas makro.
Di antara ketiga tugas makro, tugas makro yang tidak disetel akan dieksekusi terlebih dahulu, jadi tugas makro ini adalah keluaran 8 Tugas makro ini tidak menghasilkan tugas mikro, sehingga tidak ada tugas mikro yang harus dijalankan, dan tugas makro berikutnya terus dijalankan.
Tunda eksekusi makrotask selama 100 milidetik, keluaran 9、10 , dan hasilkan tugas mikro, sehingga antrian tugas mikro saat ini memiliki tugas mikro.
Setelah tugas makro dijalankan, semua tugas mikro yang dihasilkan oleh tugas makro akan dijalankan, sehingga tugas mikro akan dieksekusi
dieksekusi
. Semua tugas mikro dalam keluaran antrian tugas 11
Eksekusi tugas makro menghasilkan 2、3、5 dengan penundaan 1000 milidetik, dan tugas mikro dihasilkan. Oleh karena itu, antrian tugas mikro saat ini memiliki tugas mikro.
makrotask akan dieksekusi. Semua tugas mikro yang dihasilkan akan menjalankan semua tugas mikro di antrian tugas mikro dan menghasilkan keluaran 4
Oleh karena itu, contoh kode di atas akan menampilkan 1, 6, 13, 7 1、6、13、7、Promise.resolve、12、8、9、10、11、2、3、5、4 4, apakah kalian melakukannya dengan benar?
Banyak teman-teman yang mungkin masih belum paham setTimeout(fn) . Bukankah sudah jelas waktu tundanya tidak diatur?
Kita dapat memahami setTimeout(fn) sebagai setTimeout(fn,0) , yang sebenarnya memiliki arti yang sama.
Kita tahu bahwa js dibagi menjadi tugas sinkron dan tugas asinkron. setTimeout(fn) adalah tugas asinkron, jadi meskipun Anda tidak menyetel waktu tunda di sini, ia akan memasuki antrean asinkron dan tidak akan dieksekusi hingga thread utama selesai. menganggur.
Penulis akan menyebutkannya lagi, apakah menurut Anda waktu tunda yang kita atur setelah setTimeout , js pasti akan dieksekusi sesuai dengan waktu tunda yang kita tetapkan, menurut saya tidak. Waktu yang kami tetapkan hanyalah agar fungsi panggilan balik dapat dijalankan, tetapi apakah thread utama bebas adalah masalah lain.
fungsi tes5() {
setTimeout(fungsi () {
console.log("setTimeout");
}, 100);
misalkan saya = 0;
sementara (benar) {
saya++;
}
}
test5(); Akankah contoh di atas pasti menghasilkan setTimeout setelah 100 milidetik? Tidak, karena thread utama kita telah memasuki loop tak terbatas dan tidak memiliki waktu untuk menjalankan tugas antrian asinkron.
GUI渲染di sini. Beberapa teman mungkin tidak memahaminya. Saya akan memperkenalkannya secara rinci di artikel tentang browser nanti.
Karena JS引擎线程dan GUI渲染线程saling eksklusif, untuk memungkinkan宏任务dan DOM任务berjalan secara tertib, browser akan memulai GUI渲染线程setelah hasil eksekusi satu宏任务dan sebelum pelaksanaan宏任务berikutnya, render halaman.
Oleh karena itu, hubungan antara tugas makro, tugas mikro, dan rendering GUI adalah sebagai berikut:
tugas makro -> tugas mikro -> rendering GUI -> tugas makro ->...
[Rekomendasi tutorial video terkait: web front end]
Di atas adalah analisis mendalam tentang JavaScript Untuk detail tentang konteks eksekusi dan mekanisme eksekusi, harap perhatikan artikel terkait lainnya di situs web PHP Mandarin untuk informasi lebih lanjut!
