8 Oktober 2020

Fetch

Javascript bisa mengirim permintaan jaringan ke server dan memuat informasi baru saat dibutuhkan.

Sebagai contoh, kita bisa menggunakan permintaan jaringan untuk:

  • Mengirimkan sebuah pesanan.
  • Memuat informasi pengguna.
  • Menerima pembaharuan terbaru dari server.
  • Dan lain sebagainya.

…Dan semua itu tanpa memuat ulang halaman!

Ada istilah umbrella AJAX(singkatan dari Asynchronous JavaScript And XML) untuk permintaan jaringan dari Javascript.

Meskipun kita tidak perlu menggunakan XML : istilah ini berasal dari masa lalu, itulah kenapa kalimat ini ada. Kamu mungkin sudah mendengar istilah ini.

Ada banyak cara untuk mengirimkan sebuah permintaan jaringan dan mendapatkan informasi dari server.

Metode fetch() itu baru dan serba guna, jadi kita bisa mulai dari sini. metode ini tidak di dukung oleh browser lama (bisa memakai polyfill), tetapi sudah didukung oleh browser yang terbaru.

sintaksis dasarnya :

let promise = fetch(url, [options])
  • urlURL yang diakses.
  • options – parameter opsional : metode, headers dll.

Jika tidak menggunakan options, dan hanya menggunakan permintaan GET sederhana,sudah bisa mengunduh isi dari url.

Browser segera memulai permintaan dan mengembalikan sebuah promise yang memanggil kode yang akan di pakai untuk mendapatkan hasil.

Untuk mendapatkan sebuah tanggapan biasanya ada dua tahap proses.

Pertama, promise, akan dikembalikan oleh fetch, diselesaikan dengan sebuah objek built-in Response kelas secepat server menanggapi headers

Di tahap ini kita bisa memeriksa status HTTP, untuk melihat apakah berhasil atau tidak, untuk memeriksa headers, tetapi belum memakai body.

Promise tidak akan dijalankan jika fetch tidak bisa membuat permintaan HTTP, contohnya saat ada masalah jaringan, atau tidak ada situs yang di arahkan. Status HTTP yang tidak normal, seperti 404 atau 500 tidak menimbulkan kesalahan.

Kita bisa melihat status HTTP di properti tanggapan:

  • status – kode status misalnya 200.
  • ok – boolean, ‘true’ jika kode status HTTP 200-299.

Sebagai contoh :

let response = await fetch(url);

if (response.ok) { // jika kode status 200-299
  //dapatkan tanggapan *body* (caranya akan di jelaskan di bawah)
  let json = await response.json();
} else {
  alert("HTTP-Error: " + response.status);
}

Kedua, untuk mendapatkan tanggapan body , kita perlu menggunakan metode pemanggilan tambahan

Response menyediakan banyak metode berbasis promise untuk mengakses body dengan berbagai format:

  • response.text() – membaca tanggapan dan mengembalikan sebagai teks,
  • response.json() – mengurai tanggapan sebagai objek JSON,
  • response.formData() – mengembalikan tanggapan sebagai objek FormData(dijelaskan di bab selanjutnya),
  • response.blob() – mengembalikan tanggapan sebagai Blob (tipe untuk data biner),
  • response.ArrayBuffer() – mengembalikan tanggapan sebagai Artikel "Arraybuffer-binary-Arrays" tidak ditemukan (representasi tingkat rendah dari data biner),
  • Sebagai tambahan, response.body adalah objek ReadableStream, hal ini memungkinkan untuk membaca bagian demi bagian body, kita akan melihat contohnya nanti.

Contohnya, mari kita dapatkan objek JSON terbaru dari commit GitHub:

let url = 'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits';
let response = await fetch(url);

let commits = await response.json(); //membaca tanggapan *body* dan menguraikan sebagai JSON

alert(commits[0].author.login);

Atau, sama saja tanpa await , menggunakan sintaksis asli promise:

fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits')
  .then(response => response.json())
  .then(commits => alert(commits[0].author.login));

Untuk mendapatkan data respon dengan format teks, gunakan await response.text() daripada .json():

let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits');

let text = await response.text(); //membaca tanggapan *body* sebagai teks

alert(text.slice(0, 80) + '...');

Sebagai contoh kasus untuk membaca dalam format biner, mari kita ambil dan menunjukkan gambar logo spesifikasi “fetch” (lihat bab Blob untuk detail tentang operasi di Blob):

let response = await fetch('/article/fetch/logo-fetch.svg');

let blob = await response.blob(); //mengunduh sebagai objek Blob

//membuat elemen <img>
let img = document.createElement('img');
img.style = 'position:fixed;top:10px;left:10px;width:100px';
document.body.append(img);

//menampilkan item
img.src = URL.createObjectURL(blob);

setTimeout(() => { //menyembunyikan setelah 3 detik
  img.remove();
  URL.revokeObjectURL(img.src);
}, 3000);
Penting:

Kita hanya bisa memilih satu dari beberapa metode untuk membaca body.

Jika kita sudah mendapatkan tanggapan dengan response.text() , maka response.json() tidak akan bekerja, karena isi body sudah di proses lebih awal.

let text = await response.text(); //tanggapan *body* sudah di konsumsi
let parsed = await response.json(); //gagal (sudah di konsumsi)

Response headers

Tanggapan headers tersedia seperti objek Map headers di response.headers.

Ini tidak seperti Map, tetapi memiliki metode yang mirip untuk mendapatkan headers individu dengan nama atau mengulangi item:

let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits');

//dapatkan satu header
alert(response.headers.get('Content-Type')); // application/json; charset=utf-8

//mengulangi semua header
for (let [key, value] of response.headers) {
  alert(`${key} = ${value}`);
}

Response headers

Untuk mengatur permintaan header di fetch , kita bisa menggunakan headers option yang memiliki sebuah objek dengan headers diluar, seperti ini:

let response = fetch(protectedUrl, {
  headers: {
    Authentication: 'secret'
  }
});

…Tetapi ada daftar headers HTTP yang di larang yang tidak boleh kita gunakan :

  • Accept-Charset, Accept-Encoding
  • Access-Control-Request-Headers
  • Access-Control-Request-Method
  • Connection
  • Content-Length
  • Cookie, Cookie2
  • Date
  • DNT
  • Expect
  • Host
  • Keep-Alive
  • Origin
  • Referer
  • TE
  • Trailer
  • Transfer-Encoding
  • Upgrade
  • Via
  • Proxy-*
  • Sec-*

Header ini memastikan HTTP yang tepat dan aman, jadi mereka khusus dikendalikan oleh browser.

POST Request

Untuk membuat sebuah permintaan POST , atau sebuah permintaan dengan metode lain, kita perlu menggunakan option fetch:

  • method – metode HTTP, misalnya POST,
  • body – permintaan body, satu dari:
    • sebuah string (misalnya dikodekan JSON),
    • objek FormData, untuk mengirimkan data sebagai form/multipart,
    • Blob/BufferSource untuk mengirimkan data biner,
    • URLSearchParams, untuk mengirimkan data di x-www-form-urlencoded pengkodean, jarang digunakan.

Format JSON adalah yang paling banyak digunakan sepanjang waktu.

Contohnya, kode ini mengirimkan objek user sebagai JSON:

let user = {
  name: 'John',
  surname: 'Smith'
};

let response = await fetch('/article/fetch/post/user', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  },
  body: JSON.stringify(user)
});

let result = await response.json();
alert(result.message);

Perlu dicatat, jika permintaan body berupa string, maka header Content-Type akan di atur text/plain;charset=UTF-8 sebagai default.

Tetapi, karena kita akan mengirim JSON kita menggunakan pilihan headers untuk mengirim application/json, Content-Type sebagai jenis data yang benar untuk data pengkodean JSON.

Mengirim sebuah gambar

Kita juga bisa mengirimkan data biner dengan fetch menggunakan objek Blob atau BufferSource.

Di contoh ini, ada <canvas> dimana kita bisa menggambar dengan memindahkan mouse diatasnya. Setiap klik di tombol “submit” mengirimkan gambar ke server:

<body style="margin:0">
  <canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas>

  <input type="button" value="Submit" onclick="submit()">

  <script>
    canvasElem.onmousemove = function(e) {
      let ctx = canvasElem.getContext('2d');
      ctx.lineTo(e.clientX, e.clientY);
      ctx.stroke();
    };

    async function submit() {
      let blob = await new promise(resolve => canvasElem.toBlob(resolve, 'image/png'));
      let response = await fetch('/article/fetch/post/image', {
        method: 'POST',
        body: blob
      });

      // *server* menanggapi dengan konfirmasi dan ukuran gambar
      let result = await response.json();
      alert(result.message);
    }

  </script>
</body>

Perlu dicatat, disini kita tidak akan mengatur header Content-Type secara manual, karena sebuah objek Blob memiliki tipe built-in (image/png, di hasilkan menjadi toBlob). Untuk tipe objek Blob menjadi nilai dari Content-Type.

Fungsi submit() bisa ditulis ulang tanpa async/await seperti ini:

function submit() {
  canvasElem.toBlob(function(blob) {
    fetch('/article/fetch/post/image', {
      method: 'POST',
      body: blob
    })
      .then(response => response.json())
      .then(result => alert(JSON.stringify(result, null, 2)))
  }, 'image/png');
}

Ringkasan

Permintaan fetch biasanya terdiri dari dua pemanggilan await:

let response = await fetch(url, options); //menyelesaikan dengan tanggapan header
let result = await response.json(); // membaca *body* sebagai json

Atau, tanpa await:

fetch(url, options)
  .then(response => response.json())
  .then(result => /* hasil proses */)

Properti tanggapan:

  • response.status – kode dari tanggapan HTTP,
  • response.oktrue jika status 200-299.
  • response.headers – seperti objek Map dengan headers HTTP.

Metode untuk mendapatkan tanggapan body:

  • response.text() – mengembalikan tanggapan sebagai teks,
  • response.json() – menguraikan tanggapan sebagai objek JSON,
  • response.formData() – mengembalikan tanggapan sebagai objek FormData ( pengkodean form/multipart , lihat bab selanjutnya),
  • response.blob() – mengembalikan tanggapan sebagai Blob (data biner dengan tipe),
  • response.ArrayBuffer() – mengembalikan tanggapan sebagai Artikel "Arraybuffer-binary-Arrays" tidak ditemukan (data biner tingkat rendah),

Option Fetch sejauh ini:

  • method – metode HTTP,
  • headers – sebuah objek dengan permintaan headers (tidak semua header di izinkan),
  • body – data untuk mengirimkan (permintaan body) sebagai objek string, FormData, BufferSource, Blob atau UrlSearchParams.

di bab selanjutnya kita akan melihat lebih banyak option dan kasus penggunaan fetch.

Tugas

Buatlah sebuah fungsi async getUsers(names), yang akan mendapatkan sebuah senarai dari Github logins, ambil pengguna dari Github dan kembalikan sebuah senarai dari pengguna GitHub.

Url Github dengan informasi pengguna yang diberikan nama USERNAME : https://api.github.com/users/USERNAME.

ini adalah contoh tes di sandbox.

Detail Penting:

  1. Harus ada satu permintaan fetch untuk setiap pengguna.
  2. Permintaan tidak boleh menunggu satu sama lain. Sehingga datanya sampai secepatnya.
  3. Jika terdapat permintaan yang gagal, atau pengguna tidak ditemukan, fungsi tersebut harus mengembalikan null dalam senarai yang dihasilkan.

Buka sandbox dengan tes.

Untuk mengambil pengguna kita membutuhkan : fetch('https://api.github.com/users/USERNAME').

Jika tanggapan memiliki status 200 panggil .json() untuk membaca objek JSON.

Sebaliknya, jika sebuah fetch gagal, atau tanggapan tidak memiliki status 200, kita akan mengembalikan null di senarai hasilnya.

Jadi ini kodenya :

async function getUsers(names) {
  let jobs = [];

  for(let name of names) {
    let job = fetch(`https://api.github.com/users/${name}`).then(
      successResponse => {
        if (successResponse.status != 200) {
          return null;
        } else {
          return successResponse.json();
        }
      },
      failResponse => {
        return null;
      }
    );
    jobs.push(job);
  }

  let results = await Promise.all(jobs);

  return results;
}

Perlu dicatat, panggilan .then() terpasang secara langsung dengan fetch, jadi saat kita memiliki tanggapan, tidak akan menunggu untuk fetch lainnya, tetapi segera mulai membaca .json().

Jika kita menggunakan await Promise.all(names.map(name => fetch(...))), dan memanggil .json() di hasilnya, maka akan menunggu semua fetch untuk menanggapi. Dengan menambahkan .json() untuk masing masing fetch, kita dapat memastikan pemanggilan fetch individu mulai membaca data sebagai JSON tanpa menunggu satu sama lain.

Ini adalah contoh bagaimana API Promise tingkat rendah masih bisa digunakan meskipun kita utamanya menggunakan async / await.

Buka solusi dengan tes di sandbox.

Peta tutorial