Cara kerja Node.js

Selesai

Unit ini menjelaskan bagaimana Node.js menangani tugas masuk ke runtime JavaScript.

Jenis tugas

Aplikasi JavaScript memiliki dua jenis tugas:

  • Tugas sinkron: Tugas-tugas ini terjadi secara berurutan. Sumber daya tidak bergantung pada sumber daya lain untuk diselesaikan. Contohnya adalah operasi matematika atau manipulasi string.
  • Asinkron: Tugas-tugas ini mungkin tidak segera diselesaikan karena bergantung pada sumber daya lain. Contohnya adalah permintaan jaringan atau operasi sistem file.

Karena Anda ingin program Anda berjalan secepat mungkin, Anda ingin mesin JavaScript dapat terus bekerja saat menunggu respons dari operasi asinkron. Untuk melakukannya, ia menambahkan tugas asinkron ke antrean tugas dan terus mengerjakan tugas berikutnya.

Mengelola antrean tugas dengan perulangan peristiwa

Node.js menggunakan arsitektur berbasis peristiwa mesin JavaScript untuk memproses permintaan asinkron. Diagram berikut menggambarkan cara kerja perulangan peristiwa V8, pada tingkat tinggi:

Diagram showing how Node J S uses an event-driven architecture where an event loop processes events and returns callbacks.

Tugas asinkron, ditandai dengan sintaks yang sesuai (ditunjukkan di bawah), ditambahkan ke perulangan peristiwa. Tugas ini mencakup pekerjaan yang akan dilakukan dan fungsi panggilan balik untuk menerima hasilnya. Ketika operasi intensif selesai, fungsi panggilan balik dipicu dengan hasilnya.

Operasi sinkron versus operasi asinkron

API Node.js menyediakan operasi asinkron dan sinkron untuk beberapa operasi yang sama seperti operasi file. Meskipun umumnya Anda harus selalu berpikir asinkron-terlebih dahulu, ada kalanya Anda mungkin menggunakan operasi sinkron.

Contohnya adalah ketika antarmuka baris perintah (CLI) membaca file dan kemudian segera menggunakan data dalam file. Dalam hal ini, Anda dapat menggunakan versi sinkron dari operasi file karena tidak ada sistem atau orang lain yang menunggu untuk menggunakan aplikasi.

Namun, jika Anda membangun server web, Anda harus selalu menggunakan versi asinkron dari operasi file untuk tidak memblokir kemampuan eksekusi utas tunggal untuk memproses permintaan pengguna lain.

Dalam pekerjaan Anda sebagai pengembang di TailWind Traders, Anda harus memahami perbedaan antara operasi sinkron dan asinkron dan kapan menggunakan masing-masing operasi.

Performa melalui operasi asinkron

Node.js memanfaatkan sifat unik javaScript berbasis peristiwa yang membuat tugas server penulisan cepat dan berkinerja tinggi. JavaScript, ketika digunakan dengan benar dengan teknik asinkron, dapat menghasilkan hasil performa yang sama dengan bahasa tingkat rendah seperti C karena peningkatan performa yang dimungkinkan oleh mesin V8.

Teknik asinkron hadir dalam 3 gaya, yang perlu Anda kenali dalam pekerjaan Anda:

  • Asinkron/tunggu (disarankan): Teknik asinkron terbaru yang menggunakan async kata kunci dan await untuk menerima hasil operasi asinkron. Asinkron/tunggu digunakan di banyak bahasa pemrograman. Umumnya, proyek baru dengan dependensi yang lebih baru akan menggunakan gaya kode asinkron ini.
  • Panggilan balik: Teknik asinkron asli yang menggunakan fungsi panggilan balik untuk menerima hasil operasi asinkron. Anda akan melihat ini di basis kode yang lebih lama dan di API Node.js yang lebih lama.
  • Promises: Teknik asinkron yang lebih baru yang menggunakan objek janji untuk menerima hasil operasi asinkron. Anda akan melihat ini di basis kode yang lebih baru dan di API Node.js yang lebih baru. Anda mungkin harus menulis kode berbasis janji dalam pekerjaan Anda untuk membungkus API lama yang tidak akan diperbarui. Dengan menggunakan janji untuk pembungkusan ini, Anda mengizinkan kode untuk digunakan dalam berbagai proyek versi Node.js yang lebih besar daripada dalam gaya asinkron/menunggu kode yang lebih baru.

Asinkron/menunggu

Asinkron/menunggu adalah cara terbaru untuk menangani pemrograman asinkron. Asinkron/menunggu adalah gula sintaktik di atas janji dan membuat kode asinkron terlihat lebih seperti kode sinkron. Lebih mudah juga dibaca dan dirawat.

Contoh yang sama menggunakan asinkron/menunggu terlihat seperti ini:

// async/await asynchronous example

const fs = require('fs').promises;

const filePath = './file.txt';

// `async` before the parent function
async function readFileAsync() {
  try {
    // `await` before the async method
    const data = await fs.readFile(filePath, 'utf-8');
    console.log(data);
    console.log('Done!');
  } catch (error) {
    console.log('An error occurred...: ', error);
  }
}

readFileAsync()
  .then(() => {
    console.log('Success!');
  })
  .catch((error) => {
    console.log('An error occurred...: ', error);
  });

Ketika asinkron/menunggu dirilis pada ES2017, kata kunci hanya dapat digunakan dalam fungsi dengan fungsi tingkat atas menjadi janji. Meskipun janji tidak harus memiliki then bagian dan catch , masih diharuskan untuk memiliki promise sintaks untuk dijalankan.

Fungsi async selalu mengembalikan janji, bahkan jika tidak memiliki await panggilan di dalamnya. Janji akan diselesaikan dengan nilai yang dikembalikan oleh fungsi . Jika fungsi melemparkan kesalahan, janji akan ditolak dengan nilai yang dilemparkan.

Promises

Karena panggilan balik berlapis bisa sulit dibaca dan dikelola, Node.js menambahkan dukungan untuk janji. Janji adalah objek yang mewakili penyelesaian akhir (atau kegagalan) dari operasi asinkron.

Fungsi promise memiliki format:

// Create a basic promise function
function promiseFunction() {
  return new Promise((resolve, reject) => {
    // do something

    if (error) {
      // indicate success
      reject(error);
    } else {
      // indicate error
      resolve(data);
    }
  });
}

// Call a basic promise function
promiseFunction()
  .then((data) => {
    // handle success
  })
  .catch((error) => {
    // handle error
  });

Metode then ini dipanggil ketika janji dipenuhi dan metode dipanggil catch ketika janji ditolak.

Untuk membaca file secara asinkron dengan janji, kodenya adalah:

// promises asynchronous example

const fs = require('fs').promises;
const filePath = './file.txt';

// request to read a file
fs.readFile(filePath, 'utf-8')
  .then((data) => {
    console.log(data);
    console.log('Done!');
  })
  .catch((error) => {
    console.log('An error occurred...: ', error);
  });

console.log(`I'm the last line of the file!`);

Asinkron/tunggu tingkat atas

Versi terbaru Node.js menambahkan asinkron/tunggu tingkat atas untuk modul ES6. Anda perlu menambahkan properti bernama type di package.json dengan nilai module untuk menggunakan fitur ini.

{
    "type": "module"
}

Kemudian Anda dapat menggunakan await kata kunci di tingkat atas kode Anda

// top-level async/await asynchronous example

const fs = require('fs').promises;

const filePath = './file.txt';

// `async` before the parent function
try {
  // `await` before the async method
  const data = await fs.readFile(filePath, 'utf-8');
  console.log(data);
  console.log('Done!');
} catch (error) {
  console.log('An error occurred...: ', error);
}
console.log("I'm the last line of the file!");

Panggilan balik

Ketika Node.js awalnya dirilis, pemrograman asinkron ditangani dengan menggunakan fungsi panggilan balik. Panggilan balik adalah fungsi yang diteruskan sebagai argumen ke fungsi lain. Ketika tugas selesai, fungsi panggilan balik dipanggil.

Urutan parameter fungsi penting. Fungsi panggilan balik adalah parameter terakhir dari fungsi.

// Callback function is the last parameter
function(param1, param2, paramN, callback)

Nama fungsi dalam kode yang Anda pertahankan mungkin tidak disebut callback. Ini bisa dipanggil cb atau atau donenext. Nama fungsi tidak penting, tetapi urutan parameter penting.

Perhatikan tidak ada indikasi sindikasi bahwa fungsinya asinkron. Anda harus tahu bahwa fungsi ini asinkron dengan membaca dokumentasi atau terus membaca kode.

Contoh panggilan balik dengan fungsi panggilan balik bernama

Kode berikut memisahkan fungsi asinkron dari panggilan balik. Ini mudah dibaca dan dipahami dan memungkinkan Anda menggunakan kembali panggilan balik untuk fungsi asinkron lainnya.

// callback asynchronous example

// file system module from Node.js
const fs = require('fs');

// relative path to file
const filePath = './file.txt';

// callback
const callback = (error, data) => {
  if (error) {
    console.log('An error occurred...: ', error);
  } else {
    console.log(data); // Hi, developers!
    console.log('Done!');
  }
};

// async request to read a file
//
// parameter 1: filePath
// parameter 2: encoding of utf-8
// parmeter 3: callback function
fs.readFile(filePath, 'utf-8', callback);

console.log("I'm the last line of the file!");

Hasil yang benar adalah:

I'm the last line of the file!
Hi, developers!
Done!

Pertama, fungsi fs.readFile asinkron dimulai dan masuk ke perulangan peristiwa. Kemudian, eksekusi kode berlanjut ke baris kode berikutnya, yang merupakan yang terakhir console.log. Setelah file dibaca, fungsi panggilan balik dipanggil dan dua pernyataan console.log dijalankan.

Contoh panggilan balik dengan fungsi anonim

Contoh berikut menggunakan fungsi panggilan balik anonim, yang berarti fungsi tidak memiliki nama dan tidak dapat digunakan kembali oleh fungsi anonim lainnya.

// callback asynchronous example

// file system module from Node.js
const fs = require('fs');

// relative path to file
const filePath = './file.txt';

// async request to read a file
//
// parameter 1: filePath
// parameter 2: encoding of utf-8
// parmeter 3: callback function () => {}
fs.readFile(filePath, 'utf-8', (error, data) => {
  if (error) {
    console.log('An error occurred...: ', error);
  } else {
    console.log(data); // Hi, developers!
    console.log('Done!');
  }
});

console.log("I'm the last line of the file!");

Hasil yang benar adalah:

I'm the last line of the file!
Hi, developers!
Done!

Ketika kode dijalankan, fungsi fs.readFile asinkron dimulai dan masuk ke perulangan peristiwa. Selanjutnya eksekusi berlanjut ke baris kode berikut, yang merupakan yang terakhir console.log. Ketika file dibaca, fungsi panggilan balik dipanggil dan dua pernyataan console.log dijalankan.

Panggilan balik berlapis

Karena Anda mungkin perlu memanggil panggilan balik asinkron berikutnya dan kemudian yang lain, kode panggilan balik mungkin menjadi berlapis. Ini disebut callback hell dan sulit dibaca dan dipertahankan.

// nested callback example

// file system module from Node.js
const fs = require('fs');

fs.readFile(param1, param2, (error, data) => {
  if (!error) {
    fs.writeFile(paramsWrite, (error, data) => {
      if (!error) {
        fs.readFile(paramsRead, (error, data) => {
          if (!error) {
            // do something
          }
        });
      }
    });
  }
});

API Sinkron

Node.js juga memiliki sekumpulan API sinkron. API ini memblokir eksekusi program hingga tugas selesai. API sinkron berguna saat Anda ingin membaca file lalu segera menggunakan data dalam file.

Fungsi sinkron (pemblokiran) di Node.js menggunakan konvensi penamaan functionSync. Misalnya, API asinkron readFile memiliki mitra sinkron bernama readFileSync. Penting untuk menegakkan standar ini dalam proyek Anda sendiri sehingga kode Anda mudah dibaca dan dipahami.

// synchronous example

const fs = require('fs');

const filePath = './file.txt';

try {
  // request to read a file
  const data = fs.readFileSync(filePath, 'utf-8');
  console.log(data);
  console.log('Done!');
} catch (error) {
  console.log('An error occurred...: ', error);
}

Sebagai pengembang baru di TailWind Traders, Anda mungkin diminta untuk memodifikasi semua jenis kode Node.js. Penting untuk memahami perbedaan antara API sinkron dan asinkron, dan sintaks yang berbeda untuk kode asinkron.