JavaScript Asinkron: Panggilan balik dan janji dijelaskan

Berurusan dengan kod asinkron, yang bermaksud kod yang tidak dapat dilaksanakan dengan segera seperti permintaan atau pemasa web, boleh menjadi rumit. JavaScript memberi kita dua cara di luar kotak untuk menangani tingkah laku tidak segerak: panggilan balik dan janji.

Panggilan balik adalah satu-satunya cara yang disokong secara asli untuk menangani kod async sehingga 2016, ketika Promiseobjek diperkenalkan ke bahasa. Walau bagaimanapun, pembangun JavaScript telah melaksanakan fungsi serupa pada tahun-tahun mereka sendiri sebelum janji-janji tiba di tempat kejadian. Mari kita lihat beberapa perbezaan antara panggilan balik dan janji, dan lihat bagaimana kita menangani penyelarasan pelbagai janji.

Fungsi tak segerak yang menggunakan panggilan balik mengambil fungsi sebagai parameter, yang akan dipanggil setelah pekerjaan selesai. Sekiranya anda menggunakan sesuatu seperti setTimeoutdalam penyemak imbas, anda telah menggunakan panggilan balik.

// Anda boleh menentukan panggilan balik anda secara berasingan ...

biarkan myCallback = () => {

  console.log ('Dipanggil!');

};

setTimeout (myCallback, 3000);

// ... tetapi juga biasa untuk melihat panggilan balik ditentukan secara sebaris

setTimeout (() => {

  console.log ('Dipanggil!');

}, 3000);

Biasanya fungsi yang mengambil panggilan balik menjadikannya sebagai hujah terakhir. Ini bukan perkara di atas, jadi mari kita berpura-pura ada fungsi baru yang disebut waitseperti setTimeouttetapi mengambil dua argumen pertama dalam urutan yang bertentangan:

// Kami akan menggunakan fungsi baru kami seperti ini:

tungguCallback (3000, () => {

  console.log ('Dipanggil!');

});

Panggilan balik bersarang dan piramid azab

Panggilan balik berfungsi dengan baik untuk menangani kod tak segerak, tetapi ia menjadi rumit apabila anda mula menyelaraskan beberapa fungsi tak segerak. Sebagai contoh, jika kita mahu menunggu dua saat dan mencatat sesuatu, kemudian tunggu tiga saat dan log sesuatu yang lain, kemudian tunggu empat saat dan log sesuatu yang lain, sintaks kita menjadi sangat bersarang.

// Kami akan menggunakan fungsi baru kami seperti ini:

tungguCallback (2000, () => {

  console.log ('Callback Pertama!');

  tungguCallback (3000, () => {

    console.log ('Panggilan Balik Kedua!');

    tungguCallback (4000, () => {

      console.log ('Panggilan Balik Ketiga!');

    });

  });

});

Ini mungkin kelihatan seperti contoh remeh (dan memang demikian), tetapi tidak jarang membuat beberapa permintaan web berturut-turut berdasarkan hasil pengembalian permintaan sebelumnya. Sekiranya pustaka AJAX anda menggunakan panggilan balik, anda akan melihat struktur di atas habis. Dalam contoh-contoh yang lebih bersarang, anda akan melihat apa yang disebut sebagai piramid azab, yang mendapat namanya dari bentuk piramid yang dibuat di ruang putih lekukan pada awal garis.

Seperti yang anda lihat, kod kami akan tersusun secara struktur dan sukar dibaca ketika berhadapan dengan fungsi tak segerak yang perlu berlaku secara berurutan. Tetapi semakin sukar. Bayangkan jika kita ingin memulakan tiga atau empat permintaan web dan melakukan beberapa tugas hanya setelah semuanya kembali. Saya mendorong anda untuk mencuba melakukannya jika anda tidak pernah menghadapi cabaran sebelumnya.

Lebih mudah bersinkron dengan janji

Janji menyediakan API yang lebih fleksibel untuk menangani tugas tidak segerak. Ia memerlukan fungsi ditulis sedemikian rupa sehingga mengembalikan Promiseobjek, yang mempunyai beberapa ciri standard untuk menangani tingkah laku berikutnya dan mengkoordinasikan beberapa janji. Sekiranya waitCallbackfungsi kita Promiseberdasarkan, hanya perlu satu argumen, iaitu milisaat yang perlu ditunggu. Sebarang fungsi seterusnya akan terikat dengan janji. Contoh pertama kami akan kelihatan seperti ini:

biarkan myHandler = () => {

  console.log ('Dipanggil!');

};

waitPromise (3000). kemudian (myHandler);

Dalam contoh di atas, waitPromise(3000)mengembalikan Promiseobjek yang mempunyai beberapa kaedah untuk kita gunakan, seperti then. Sekiranya kita ingin melaksanakan beberapa fungsi tak segerak satu demi satu, kita dapat mengelakkan piramid azab dengan menggunakan janji. Kod itu, ditulis semula untuk menyokong janji baru kami, akan kelihatan seperti ini:

// Tidak kira berapa banyak tugas async berurutan yang kita ada, kita tidak pernah membuat piramid.

tunggu Janji (2000)

  .then (() => {

    console.log ('Callback Pertama!');

    kembali menungguPromise (3000);

  })

  .then (() => {

    console.log ('Panggilan Balik Kedua!');

    kembali menungguPromise (4000);

  })

  .then (() => {

    console.log ('Panggilan Balik Kedua!');

    kembali menungguPromise (4000);

  });

Lebih baik lagi, jika kita perlu mengkoordinasikan tugas asinkron yang mendukung Janji, kita dapat menggunakan all, yang merupakan kaedah statis pada Promiseobjek yang mengambil beberapa janji dan menggabungkannya menjadi satu. Itu akan kelihatan seperti:

Promise.all ([

  waitPromise (2000),

  tunggu Janji (3000),

  tunggu Janji (4000)

]). kemudian (() => console.log ('Semuanya selesai!'));

Minggu depan, kami akan membahas lebih lanjut mengenai bagaimana janji berfungsi dan bagaimana menggunakannya secara idiomatik. Sekiranya anda baru belajar JavaScript atau anda berminat untuk menguji pengetahuan anda, cubalah waitCallbackatau cuba capai setara Promise.alldengan panggilan balik.

Seperti biasa, hubungi saya di Twitter dengan sebarang komen atau pertanyaan.