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.
Promise
biasanya 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 reject
reject
;
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 .then
kode 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
Promise
baru harus dikembalikan dalam metode.then
di setiap langkah, jika tidak,Promise
lama sebelumnya akan diterima.Hal ini karena masing-
then
metode akan terus meneruskanPromise
ke bawah.