Pemrograman asinkron dengan async / menunggu

Selamat siang teman!

Tambahan JavaScript yang relatif baru adalah fungsi asinkron dan kata kunci yang menunggu. Fitur-fitur ini pada dasarnya sintaksis gula lebih dari janji (janji), membuatnya mudah untuk menulis dan membaca kode asinkron. Mereka membuat kode asinkron terlihat seperti sinkron. Artikel ini akan membantu Anda mengetahui apa itu.

Ketentuan: melek komputer dasar, pengetahuan tentang dasar-dasar JS, memahami dasar-dasar kode dan janji asinkron.
Tujuan: Untuk memahami bagaimana janji dibuat dan bagaimana janji itu digunakan.

Async / tunggu dasar-dasar


Menggunakan async / await memiliki dua bagian.

Kata kunci async


Pertama-tama, kita memiliki kata kunci async, yang kita tempatkan sebelum deklarasi fungsi untuk menjadikannya tidak sinkron. Fungsi asinkron adalah fungsi yang mengantisipasi kemampuan untuk menggunakan kata kunci yang menunggu untuk menjalankan kode asinkron.

Coba ketikkan yang berikut ini di konsol browser:

function hello(){ return 'Hello' }
hello()

Fungsi akan mengembalikan 'Hello'. Tidak ada yang aneh, bukan?

Tetapi bagaimana jika kita mengubahnya menjadi fungsi yang tidak sinkron? Coba yang berikut ini:

async function hello(){ return 'Hello' }
hello()

Sekarang panggilan fungsi mengembalikan janji. Ini adalah salah satu fitur fungsi asinkron - mereka mengembalikan nilai yang dijamin akan dikonversi ke janji.

Anda juga dapat membuat ekspresi fungsional asinkron, seperti:

let hello = async function(){ return hello() }
hello()

Anda juga dapat menggunakan fungsi panah:

let hello = async () => { return 'Hello' }

Semua fungsi ini melakukan hal yang sama.

Untuk mendapatkan nilai janji yang sudah selesai, kita dapat menggunakan blok .then ():

hello().then((value) => console.log(value))

... atau bahkan lebih:

hello().then(console.log)

Jadi, menambahkan kata kunci async menyebabkan fungsi mengembalikan janji alih-alih nilai. Selain itu, memungkinkan fungsi sinkron untuk menghindari overhead yang terkait dengan menjalankan dan mendukung penggunaan menunggu. Tambahan async di depan fungsi memungkinkan pengoptimalan kode otomatis oleh mesin JS. Keren!

Kata kunci menunggu


Manfaat fungsi asinkron menjadi lebih jelas ketika Anda menggabungkannya dengan kata kunci yang menunggu. Itu dapat ditambahkan sebelum fungsi berbasis janji untuk membuatnya menunggu janji untuk menyelesaikan, dan kemudian mengembalikan hasilnya. Setelah itu, blok kode selanjutnya dieksekusi.

Anda dapat menggunakan menunggu saat memanggil fungsi apa pun yang mengembalikan janji, termasuk fungsi API Web.

Berikut ini adalah contoh sepele:

async function hello(){
    return greeting = await Promise.resolve('Hello')
}

hello().then(alert)

Tentu saja, kode di atas tidak berguna, hanya berfungsi sebagai demonstrasi sintaksis. Mari kita beralih dan melihat contoh nyata.

Menulis ulang kode pada janji menggunakan async / tunggu


Ambil contoh ambil dari artikel sebelumnya:

fetch('coffee.jpg')
.then(response => response.blob())
.then(myBlob => {
    let objectURL = URL.createObjectURL(myBlob)
    let image = document.createElement('img')
    image.src = objectURL
    document.body.appendChild(image)
})
.catch(e => {
    console.log('There has been a problem with your fetch operation: ' + e.message)
})

Anda seharusnya sudah memiliki pemahaman tentang apa itu janji dan bagaimana mereka bekerja, tetapi mari kita menulis ulang kode ini menggunakan async / menunggu untuk melihat betapa sederhananya itu:

async function myFetch(){
    let response = await fetch('coffee.jpg')
    let myBlob = await response.blob()

    let objectURL = URL.createObjectURL(myBlob)
    let image = document.createElement('img')
    image.src = objectURL
    document.body.appendChild(image)
}

myFetch().catch(e => {
    console.log('There has been a problem with your fetch operation: ' + e.message)
})

Ini membuat kode lebih sederhana dan lebih mudah dipahami - bukan .then () blok!

Menggunakan kata kunci async mengubah fungsi menjadi janji, sehingga kita dapat menggunakan pendekatan campuran dari janji dan menunggu, menyoroti bagian kedua fungsi dalam blok terpisah untuk meningkatkan fleksibilitas:

async function myFetch(){
    let response = await fetch('coffee.jpg')
    return await response.blob()
}

myFetch().then((blob) => {
    let objectURL = URL.createObjectURL(blob)
    let image = document.createElement('image')
    image.src = objectURL
    document.body.appendChild(image)
}).catch(e => console.log(e))

Anda dapat menulis ulang contoh atau menjalankan demo langsung kami (lihat juga kode sumber ).

Tetapi bagaimana cara kerjanya?


Kami membungkus kode di dalam fungsi dan menambahkan kata kunci async sebelum kata kunci fungsi. Anda perlu membuat fungsi asinkron untuk menentukan blok kode di mana kode asinkron akan dijalankan; menunggu hanya berfungsi di dalam fungsi asinkron.

Sekali lagi: tunggu hanya berfungsi dalam fungsi asinkron.

Di dalam fungsi myFetch (), kode ini sangat mirip dengan versi yang dijanjikan, tetapi dengan beberapa perbedaan. Alih-alih menggunakan blok .then () setelah setiap metode berbasis janji, tambahkan saja kata kunci tunggu sebelum memanggil metode dan berikan nilai pada variabel. Kata kunci yang menunggu menyebabkan mesin JS untuk menghentikan eksekusi kode pada baris yang diberikan, memungkinkan kode lain untuk mengeksekusi sampai fungsi asinkron mengembalikan hasil. Setelah dieksekusi, kode akan melanjutkan eksekusi dari baris berikutnya.
Contohnya:

let response = await fetch('coffee.jpg')

Nilai yang dikembalikan oleh janji ambil () ditugaskan ke variabel respons ketika nilai yang diberikan tersedia, dan parser berhenti di baris itu sampai janji selesai. Segera setelah nilai tersedia, parser beralih ke baris kode berikutnya yang menciptakan Blob. Baris ini juga memanggil metode asinkron berbasis janji, jadi di sini kami juga menggunakan menunggu. Ketika hasil operasi kembali, kami mengembalikannya dari fungsi myFetch ().

Ini berarti bahwa ketika kita memanggil fungsi myFetch (), ia mengembalikan sebuah janji, sehingga kita dapat menambahkan .then () ke dalamnya, di dalamnya kita menangani tampilan gambar di layar.

Anda mungkin berpikir "Itu hebat!" Dan Anda benar - lebih sedikit. Kemudian () blok untuk membungkus kode, semuanya terlihat seperti kode sinkron, jadi itu intuitif.

Tambahkan penanganan kesalahan


Jika Anda ingin menambahkan penanganan kesalahan, Anda memiliki beberapa opsi.

Anda dapat menggunakan struktur coba ... tangkap dengan async / tunggu. Contoh ini adalah versi lanjutan dari kode di atas:

async function myFetch(){
    try{
        let response = await fetch('coffee.jpg')
        let myBlob = await response.blob()

        let objectURL = URL.createObjectURL(myBlob)
        let image = document.createElement('img')
        image.src = objectURL
        document.body.appendChild(image)
    } catch(e){
        console.log(e)
    }
}

myFetch()

Blok catch () {} menerima objek kesalahan, yang kami beri nama "e"; Sekarang kita dapat menampilkannya ke konsol, ini akan memungkinkan kita untuk menerima pesan tentang di mana kesalahan terjadi dalam kode.

Jika Anda ingin menggunakan versi kedua dari kode yang ditunjukkan di atas, Anda harus terus menggunakan pendekatan hybrid dan menambahkan blok .catch () ke akhir panggilan .then (), sebagai berikut:

async function myFetch(){
    let response = await fecth('coffee.jpg')
    return await response.blob()
}

myFetch().then((blob) => {
    let objectURL = URL.createObjectURL
    let image = document.createElement('img')
    image.src = objectURL
    document.body.appendChild(image)
}).catch(e => console.log(e))

Ini dimungkinkan karena blok .catch () akan menangkap kesalahan yang terjadi baik dalam fungsi asinkron maupun dalam rantai janji. Jika Anda menggunakan blok coba / tangkap di sini, Anda tidak akan dapat menangani kesalahan yang terjadi ketika fungsi myFetch () dipanggil.

Anda dapat menemukan kedua contoh di GitHub:
simple-fetch-async-await-try-catch.html (lihat sumber )

simple-fetch-async-await-janji-catch.html (lihat sumber )

Menunggu Janji. Semua ()


Async / menunggu didasarkan pada janji, sehingga Anda dapat mengambil keuntungan penuh dari yang terakhir. Ini termasuk Promise.all () khususnya - Anda dapat dengan mudah menambahkan menunggu ke Promise.all () untuk menulis semua nilai pengembalian dengan cara yang mirip dengan kode sinkron. Sekali lagi, ambil contoh dari artikel sebelumnya . Biarkan tab terbuka dengannya untuk dibandingkan dengan kode yang ditunjukkan di bawah ini.

Dengan async / await (lihat demo langsung dan kode sumber ) tampilannya seperti ini:

async function fetchAndDecode(url, type){
    let repsonse = await fetch(url)

    let content

    if(type === 'blob'){
        content = await response.blob()
    } else if(type === 'text'){
        content = await response.text()
    }

    return content
}

async function displayContent(){
    let coffee = fetchAndDecode('coffee.jpg', 'blob')
    let tea = fetchAndDecode('tea.jpg', 'blob')
    let description = fetchAndDecode('description.txt', 'text')

    let values = await Promise.all([coffee, tea, description])

    let objectURL1 = URL.createObjectURL(values[0])
    let objectURL2 = URL.createObjectURL(values[1])
    let descText = values[2]

    let image1 = document.createElement('img')
    let image2 = document.createElement('img')
    image1.src = objectURL1
    image2.src = objectURL2
    document.body.appendChild(image1)
    document.body.appendChild(image2)

    let para = document.createElement('p')
    para.textContent = descText
    document.body.appendChild(para)
}

displayContent()
.catch(e => console.log(e))

Kami dengan mudah mengubah fungsi fetchAndDecode () menjadi asinkron dengan beberapa perubahan. Perhatikan garis:

let values = await Promise.all([coffee, tea, description])

Menggunakan menunggu, kami mendapatkan hasil dari tiga janji dalam variabel nilai, dengan cara yang mirip dengan kode sinkron. Kita harus membungkus seluruh fungsi dalam fungsi asinkron baru, displayContent (). Kami tidak mencapai pengurangan kode yang kuat, tetapi dapat mengekstraksi sebagian besar kode dari blok .then (), yang memberikan penyederhanaan yang berguna dan membuat kode lebih mudah dibaca.

Untuk menangani kesalahan, kami menambahkan blok .catch () ke panggilan kami ke displayContent (); Ini menangani kesalahan kedua fungsi.

Ingat: Anda juga dapat menggunakan blok .finally () untuk mendapatkan laporan tentang operasi - Anda dapat melihatnya beraksi di demo langsung kami (lihat juga kode sumber ).

Kerugian Async / menunggu


Async / menunggu memiliki beberapa kekurangan.

Async / wait membuat kode terlihat seperti sinkron dan, dalam arti tertentu, membuatnya berperilaku lebih sinkron. Kata kunci yang menunggu memblokir eksekusi kode yang mengikutinya sampai janji selesai, seperti yang terjadi dalam operasi sinkron. Ini memungkinkan Anda untuk melakukan tugas lain, tetapi kode Anda sendiri dikunci.

Ini berarti bahwa kode Anda dapat diperlambat oleh sejumlah besar janji yang tertunda mengikuti satu demi satu. Setiap menunggu akan menunggu penyelesaian yang sebelumnya, sementara kami ingin janji-janji dipenuhi secara bersamaan, seolah-olah kami tidak menggunakan async / menunggu.

Ada pola desain untuk mengatasi masalah ini - menonaktifkan semua proses janji dengan menyimpan objek Janji dalam variabel dan kemudian menunggu mereka. Mari kita lihat bagaimana ini diterapkan.

Kami memiliki dua contoh yang tersedia: slow-async-await.html (lihat kode sumber ) dan fast-async-await.html (lihat kode sumber ). Kedua contoh dimulai dengan fungsi janji yang meniru operasi asinkron menggunakan setTimeout ():

function timeoutPromise(interval){
    return new Promise((resolve, reject) => {
        setTimeout(function(){
            resolve('done')
        }, interval)
    })
}

Kemudian ikuti fungsi timeynest asynchronous (), yang mengharapkan tiga panggilan ke timeoutPromise ():

async function timeTest(){
    ...
}

Masing-masing dari tiga panggilan ke timeTest () berakhir dengan catatan waktu yang dibutuhkan untuk memenuhi janji, kemudian waktu yang dibutuhkan untuk menyelesaikan seluruh operasi dicatat:

let startTime = Date.now()
timeTest().then(() => {
    let finishTime = Date.now()
    let timeTaken = finishTime - startTime
    alert('Time taken in milliseconds: ' + timeTaken)
})

Dalam setiap kasus, fungsi timeTest () berbeda.

Dalam timeTest () lambat-async-await.html () terlihat seperti ini:

async function timeTest(){
    await timeoutPromise(3000)
    await timeoutPromise(3000)
    await timeoutPromise(3000)
}

Di sini kita hanya berharap tiga panggilan ke timeoutPromise, setiap kali mengatur penundaan 3 detik. Setiap panggilan menunggu penyelesaian yang sebelumnya - jika Anda menjalankan contoh pertama, Anda akan melihat jendela modal dalam waktu sekitar 9 detik.

Di fast-async-await.html timeTest () terlihat seperti ini:

async function timeTest(){
    const timeoutPromise1 = timeoutPromise(3000)
    const timeoutPromise2 = timeoutPromise(3000)
    const timeoutPromise3 = timeoutPromise(3000)

    await timeoutPromise1
    await timeoutPromise2
    await timeoutPromise3
}

Di sini kita menyimpan tiga objek Janji dalam variabel, yang menyebabkan proses yang terkait dengannya berjalan secara bersamaan.

Selanjutnya, kami mengharapkan hasil mereka - ketika janji mulai dipenuhi secara bersamaan, janji juga akan diselesaikan pada saat yang sama; ketika Anda menjalankan contoh kedua, Anda akan melihat jendela modal dalam waktu sekitar 3 detik!

Anda harus hati-hati menguji kode dan mengingatnya sambil mengurangi kinerja.

Ketidaknyamanan kecil lainnya adalah kebutuhan untuk membungkus janji yang diharapkan dalam fungsi asinkron.

Menggunakan async / tunggu dengan kelas


Sebagai kesimpulan, kami mencatat bahwa Anda dapat menambahkan async bahkan dalam metode untuk membuat kelas sehingga mereka mengembalikan janji dan menunggu janji di dalamnya. Ambil kode dari artikel tentang berorientasi objek JS dan bandingkan dengan versi yang dimodifikasi menggunakan async:

class Person{
    constructor(first, last, age, gender, interests){
        this.name = {
            first,
            last
        }
        this.age = age
        this.gender = gender
        this.interests = interests
    }

    async greeting(){
        return await Promise.resolve(`Hi! I'm ${this.name.first}`)
    }

    farewell(){
        console.log(`${this.name.first} has left the building. Bye for now!`)
    }
}

let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling'])

Metode kelas dapat digunakan sebagai berikut:

han.greeting().then(console.log)

Dukungan browser


Salah satu kendala untuk menggunakan async / menunggu adalah kurangnya dukungan untuk browser yang lebih lama. Fitur ini tersedia di hampir semua browser modern, serta janji; Beberapa masalah ada di Internet Explorer dan Opera Mini.

Jika Anda ingin menggunakan async / menunggu, tetapi membutuhkan dukungan dari browser lama, Anda dapat menggunakan perpustakaan BabelJS - ini memungkinkan Anda untuk menggunakan JS terbaru, mengonversinya menjadi yang khusus browser.

Kesimpulan


Async / await memungkinkan Anda untuk menulis kode asinkron yang mudah dibaca dan dipelihara. Meskipun async / menunggu lebih buruk didukung daripada cara lain untuk menulis kode asinkron, pasti patut ditelusuri.

Terima kasih atas perhatian Anda.

Selamat coding!

All Articles