
Asynchronous adalah untuk meningkatkan tingkat hunian CPU dan membuatnya sibuk sepanjang waktu.
Beberapa operasi (yang paling umum adalah I/O) tidak memerlukan partisipasi CPU dan sangat memakan waktu. Jika asinkron tidak digunakan, maka akan membentuk keadaan pemblokiran, menyebabkan CPU menganggur dan halaman terhenti.
Ketika operasi I/O terjadi di lingkungan asinkron, CPU mengesampingkan pekerjaan I/O (saat ini, I/O diambil alih oleh pengontrol lain dan data masih dikirim), dan kemudian memproses tugas berikutnya , menunggu operasi I/O selesai. Beritahu CPU (panggilan balik adalah metode pemberitahuan) untuk kembali bekerja.
Isi inti dari "JavaScript Asynchronous dan Callback" adalah bahwa waktu akhir spesifik dari pekerjaan asynchronous tidak pasti Untuk melakukan pemrosesan selanjutnya secara akurat setelah pekerjaan asynchronous selesai, callback perlu diteruskan ke fungsi asynchronous, sehingga Setelahnya. menyelesaikan pekerjaan Anda, lanjutkan dengan tugas-tugas berikut.
Meskipun callback bisa sangat sederhana untuk diimplementasikan secara asinkron, callback tersebut dapat membentuk callback hell karena banyak sarang. Untuk menghindari panggilan balik yang buruk, Anda perlu mengubah dan mengubah pemrograman bersarang menjadi pemrograman linier.
Promise adalah solusi terbaik untuk menangani panggilan balik neraka di JavaScript .
Promise dapat diterjemahkan sebagai "promise". Kita dapat merangkum pekerjaan asynchronous dan menyebutnya sebagai Promise , yaitu membuat janji dan berjanji untuk memberikan sinyal yang jelas setelah pekerjaan asynchronous berakhir!
Sintaks Promise :
biarkan janji = janji baru(fungsi(putuskan, tolak){
// Pekerjaan asinkron}) Melalui sintaks di atas, kita dapat merangkum pekerjaan asinkron menjadi sebuah Promise . Fungsi yang diteruskan saat membuat Promise adalah metode untuk menangani pekerjaan asinkron, yang juga dikenal sebagai executor .
resolve dan reject adalah fungsi panggilan balik yang disediakan oleh JavaScript itu sendiri. Fungsi tersebut dapat dipanggil ketika executor menyelesaikan tugas:
resolve(result) - jika berhasil diselesaikan, result akan dikembalikanreject(error) - jika error gagal dan error akan dihasilkan;executor akan secara otomatis mengeksekusi segera setelah Promise dibuat, dan status eksekusinya akan mengubah status properti internal Promise :
state - awalnya pending , kemudian diubah menjadi fulfilled setelah resolve dipanggil, atau rejected ketika reject dipanggil;result ——Awalnya undefined , dan kemudian menjadi value setelah resolve(value) dipanggil, atau menjadi error setelah reject dipanggilfs.readFile fungsi asinkron . Kita dapat meneruskannya ke executor Operasi pembacaan file dilakukan di file, sehingga merangkum pekerjaan asinkron.
Kode berikut merangkum fungsi fs.readFile , dan menggunakan resolve(data) untuk menangani hasil yang sukses dan reject(err) untuk menangani hasil yang gagal.
Kodenya sebagai berikut:
let janji = janji baru((putuskan, tolak) => {
fs.readFile('1.txt', (err, data) => {
console.log('Baca 1.txt')
jika (err) menolak (err)
tekad(data)
})}) Jika kita mengeksekusi kode ini, akan muncul tulisan "Read 1.txt", yang membuktikan bahwa operasi pembacaan file dilakukan segera setelah Promise dibuat.
Promisebiasanya merangkum kode asinkron secara internal, tetapi tidak hanya merangkum kode asinkron.
Kasus Promise di atas merangkum operasi pembacaan file. File akan dibaca segera setelah pembuatan selesai. Jika Anda ingin mendapatkan hasil eksekusi Promise , Anda perlu menggunakan tiga metode then , catch , dan finally .
Metode Promise then dapat digunakan untuk menangani pekerjaan setelah eksekusi Promise selesai. Ia menerima dua parameter panggilan balik. Sintaksnya adalah sebagai berikut:
janji.lalu(fungsi(hasil),fungsi(kesalahan))
result adalah nilai yang diterima oleh resolve ;error adalah parameter yang diterima rejectreject
;
janji = Janji baru((putuskan, tolak) => {
fs.readFile('1.txt', (err, data) => {
console.log('Baca 1.txt')
jika (err) menolak (err)
tekad(data)
})})janji.lalu(
(data) => {
console.log('Berhasil dijalankan, hasilnya' + data.toString())
},
(salah) => {
console.log('Eksekusi gagal, ada kesalahan' + err.message)
}) Jika pembacaan file berhasil dijalankan, fungsi pertama akan dipanggil:
PS E:CodeNodedemos 3-callback> node .index.js Baca 1.txt Jika berhasil dieksekusi, hasilnya 1
terhapus 1.txt . Jika eksekusi gagal, fungsi kedua akan dipanggil:
PS E:CodeNodedemos 3-callback> node .index.js Baca 1.txt Eksekusi gagal dengan kesalahan ENOENT: tidak ada file atau direktori seperti itu, buka 'E:CodeNodedemos 3-callback1.txt'
Jika kita hanya fokus pada hasil eksekusi yang berhasil, kita hanya dapat meneruskan satu fungsi panggilan balik:
janji .then((data)=>{
console.log('Berhasil dijalankan, hasilnya' + data.toString())}) Pada titik ini kami telah menerapkan operasi pembacaan file yang tidak sinkron.
Jika kita hanya fokus pada hasil kegagalan, kita dapat meneruskan null ke panggilan balik then : promise.then(null,(err)=>{...}) .
Atau gunakan cara yang lebih elegan: promise.catch((err)=>{...})
let janji = new Promise((resolve, reject) => {
fs.readFile('1.txt', (err, data) => {
console.log('Baca 1.txt')
jika (err) menolak (err)
tekad(data)
})})janji.catch((err)=>{
console.log(err.message)}) .catch((err)=>{...}) dan then(null,(err)=>{...}) memiliki efek yang persis sama.
.finally adalah fungsi yang akan dieksekusi terlepas dari hasil promise . Fungsi ini memiliki tujuan yang sama dengan finally try...catch... , dan dapat menangani operasi yang tidak terkait dengan hasilnya.
Misalnya:
Janji baru((menyelesaikan,menolak)=>{
//sesuatu...}).finally(()=>{console.log('Jalankan apa pun hasilnya')}).then(result=>{...}, err=>{...} ) Panggilan balik akhirnya tidak memiliki parameter, fs.readFile() membaca 10 file secara berurutan dan mengeluarkan isinya dari sepuluh file secara berurutan.
Karena fs.readFile() sendiri asynchronous, kita harus menggunakan callback nesting. Kodenya adalah sebagai berikut:
fs.readFile('1.txt', (err, data) => {
konsol.log(data.toString()) //1
fs.readFile('2.txt', (err, data) => {
konsol.log(data.toString())
fs.readFile('3.txt', (err, data) => {
konsol.log(data.toString())
fs.readFile('4.txt', (err, data) => {
konsol.log(data.toString())
fs.readFile('5.txt', (err, data) => {
konsol.log(data.toString())
fs.readFile('6.txt', (err, data) => {
konsol.log(data.toString())
fs.readFile('7.txt', (err, data) => {
konsol.log(data.toString())
fs.readFile('8.txt', (err, data) => {
konsol.log(data.toString())
fs.readFile('9.txt', (err, data) => {
konsol.log(data.toString())
fs.readFile('10.txt', (err, data) => {
konsol.log(data.toString())
// ==> Gerbang Neraka})
})
})
})
})
})
})
})
})}) Meskipun kode di atas dapat menyelesaikan tugas, seiring dengan meningkatnya panggilan yang bersarang, level kode menjadi lebih dalam dan kesulitan pemeliharaan meningkat, terutama ketika kita menggunakan kode nyata yang mungkin berisi banyak loop dan pernyataan kondisional, bukannya console.log(...) sederhana dalam contoh.
Jika kita tidak menggunakan callback dan langsung memanggil fs.readFile() secara berurutan sesuai kode berikut, apa yang akan terjadi?
//Catatan: Ini adalah cara menulis fs.readFile('1.txt', (err, data) yang salah => {
console.log(data.toString())})fs.readFile('2.txt', (err, data) => {
console.log(data.toString())})fs.readFile('3.txt', (err, data) => {
console.log(data.toString())})fs.readFile('4.txt', (err, data) => {
console.log(data.toString())})fs.readFile('5.txt', (err, data) => {
console.log(data.toString())})fs.readFile('6.txt', (err, data) => {
console.log(data.toString())})fs.readFile('7.txt', (err, data) => {
console.log(data.toString())})fs.readFile('8.txt', (err, data) => {
console.log(data.toString())})fs.readFile('9.txt', (err, data) => {
console.log(data.toString())})fs.readFile('10.txt', (err, data) => {
console.log(data.toString())}) Berikut hasil pengujian saya (hasil tiap eksekusi berbeda-beda):
PS E:CodeNodedemos 3-callback> node .indexalasan
js12346957108
menghasilkan hasil non-sekuensial ini adalah asynchronous , bukan paralelisme multi-threaded.
Alasan mengapa kasus kesalahan ini digunakan di sini adalah untuk menekankan konsep asinkron. Jika Anda tidak memahami mengapa hasil ini terjadi, Anda harus kembali dan memperbaiki pelajarannya!
Gagasan menggunakan Promise untuk menyelesaikan pembacaan file sekuensial asinkron:
promise1 , dan menggunakan resolve untuk mengembalikan hasilnya.promise1.then untuk menerima dan menampilkan hasil pembacaan file.promise2 baru di promise1.then . Dan kembalipromise2.then baru untuk menerima dan menampilkan hasil pembacaanpromise3 baru di promise2.then , dan kembalipromise3.then baru untuk menerima dan menampilkan hasil... kodenya sebagai berikut:
let janji1 = janji baru( (putuskan, tolak) => {
fs.readFile('1.txt', (err, data) => {
jika (err) menolak (err)
tekad(data)
})})biarkan janji2 = janji1.lalu(
data => {
konsol.log(data.toString())
kembalikan Janji baru((putuskan, tolak) => {
fs.readFile('2.txt', (err, data) => {
jika (err) menolak (err)
tekad(data)
})
})
})biarkan janji3 = janji2.lalu(
data => {
konsol.log(data.toString())
kembalikan Janji baru((putuskan, tolak) => {
fs.readFile('3.txt', (err, data) => {
jika (err) menolak (err)
tekad(data)
})
})
})biarkan janji4 = janji3.lalu(
data => {
konsol.log(data.toString())
//.....
})... ... Dengan cara ini kita menulis panggilan balik bersarang asli ke dalam mode linier.
Namun masih ada masalah dengan kodenya. Meskipun kodenya menjadi lebih indah dalam hal pengelolaan, hal ini sangat menambah panjang kode.
Kode di atas terlalu panjang. Kita dapat mengurangi jumlah kode melalui dua langkah:
promise perantara, dan menghubungkan .thenkode sebagai berikut:
function myReadFile (path) {
kembalikan Janji baru((putuskan, tolak) => {
fs.readFile(jalur, (err, data) => {
jika (err) menolak (err)
konsol.log(data.toString())
menyelesaikan()
})
})}FileBacaansaya('1.txt')
.lalu(data => { kembalikan myReadFile('2.txt') })
.lalu(data => { kembalikan myReadFile('3.txt') })
.lalu(data => { kembalikan myReadFile('4.txt') })
.lalu(data => { kembalikan myReadFile('5.txt') })
.lalu(data => { kembalikan myReadFile('6.txt') })
.lalu(data => { kembalikan myReadFile('7.txt') })
.lalu(data => { kembalikan myReadFile('8.txt') })
.lalu(data => { kembalikan myReadFile('9.txt') })
.then(data => { return myReadFile('10.txt') }) Karena metode myReadFile akan mengembalikan Promise baru, kita dapat langsung mengeksekusi metode .then . Metode pemrograman ini disebut pemrograman rantai .
Hasil eksekusi kodenya adalah sebagai berikut:
PS E:CodeNodedemos 3-callback> node .index.js12345678910
Ini menyelesaikan operasi pembacaan file asinkron dan berurutan.
Catatan: Objek
Promisebaru harus dikembalikan dalam metode.thendi setiap langkah, jika tidak,Promiselama sebelumnya akan diterima.Hal ini karena masing-
thenmetode akan terus meneruskanPromiseke bawah.