Tutorial js
Async JavaScript: Promise dan Fetch
Async JavaScript memungkinkan program menjalankan tugas tanpa harus menunggu tugas lain selesai. Bayangkan seperti restoran yang bisa melayani banyak pelanggan secara bersamaan.
Mengapa Perlu Async?
Dalam JavaScript, beberapa operasi membutuhkan waktu:
- Mengambil data dari server (API)
- Membaca file besar
- Menunggu user input
- Timer/delay tertentu
Tanpa async, program akan "freeze" menunggu operasi selesai.
Synchronous vs Asynchronous
Synchronous (Sinkron) - Menunggu Antrian
console.log("1. Mulai program");
console.log("2. Proses data...");
console.log("3. Selesai");
// Output berurutan:
// 1. Mulai program
// 2. Proses data...
// 3. Selesai
Asynchronous (Async) - Tidak Menunggu
console.log("1. Mulai program");
// setTimeout = async operation
setTimeout(function () {
console.log("2. Proses async selesai");
}, 2000);
console.log("3. Program lanjut");
// Output:
// 1. Mulai program
// 3. Program lanjut
// 2. Proses async selesai (setelah 2 detik)
Callback Function
Callback adalah function yang dipanggil setelah operasi async selesai.
function ambil_data_user(user_id, callback) {
console.log("π‘ Mengambil data user...");
// Simulasi delay network request
setTimeout(function () {
let user_data = {
id: user_id,
nama: "Ahmad Rizki",
email: "ahmad@email.com",
status: "active",
};
console.log("β
Data berhasil diambil");
callback(user_data); // Panggil callback dengan data
}, 2000);
}
function tampilkan_user(user) {
console.log("=== INFO USER ===");
console.log("ID:", user.id);
console.log("Nama:", user.nama);
console.log("Email:", user.email);
console.log("Status:", user.status);
}
// Penggunaan callback
console.log("π Memulai aplikasi...");
ambil_data_user(123, tampilkan_user);
console.log("β³ Menunggu data...");
Callback Hell Problem
// Contoh callback hell (tidak disarankan)
ambil_data_user(123, function (user) {
ambil_data_posts(user.id, function (posts) {
ambil_data_comments(posts[0].id, function (comments) {
ambil_data_likes(comments[0].id, function (likes) {
console.log("π΅βπ« Callback hell!");
// Sangat sulit dibaca dan di-maintain
});
});
});
});
Promise - Solusi Modern
Promise adalah janji bahwa operasi async akan selesai (berhasil atau gagal).
1. Membuat Promise
function ambil_cuaca(kota) {
return new Promise(function (resolve, reject) {
console.log(`π€οΈ Mengambil data cuaca ${kota}...`);
setTimeout(function () {
// Simulasi random success/error
let berhasil = Math.random() > 0.3;
if (berhasil) {
let data_cuaca = {
kota: kota,
suhu: Math.floor(Math.random() * 15) + 25, // 25-40Β°C
kondisi: "Cerah",
kelembaban: Math.floor(Math.random() * 40) + 60, // 60-100%
};
resolve(data_cuaca); // Promise berhasil
} else {
reject("β Gagal mengambil data cuaca"); // Promise gagal
}
}, 1500);
});
}
2. Menggunakan Promise dengan .then() dan .catch()
ambil_cuaca("Jakarta")
.then(function (data) {
console.log("β
Data cuaca berhasil diambil:");
console.log(`π‘οΈ Suhu: ${data.suhu}Β°C`);
console.log(`βοΈ Kondisi: ${data.kondisi}`);
console.log(`π§ Kelembaban: ${data.kelembaban}%`);
})
.catch(function (error) {
console.log("β Error:", error);
})
.finally(function () {
console.log("π Operasi selesai (berhasil atau gagal)");
});
3. Chaining Promise
function cek_login(username, password) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (username === "admin" && password === "123456") {
resolve({ id: 1, username: "admin", role: "administrator" });
} else {
reject("Username atau password salah");
}
}, 1000);
});
}
function ambil_profile(user_id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
id: user_id,
nama_lengkap: "Administrator System",
last_login: new Date().toLocaleString(),
});
}, 800);
});
}
function ambil_permissions(user_id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(["read", "write", "delete", "admin"]);
}, 600);
});
}
// Promise chaining
cek_login("admin", "123456")
.then((user) => {
console.log("β
Login berhasil:", user.username);
return ambil_profile(user.id); // Return promise baru
})
.then((profile) => {
console.log("β
Profile loaded:", profile.nama_lengkap);
return ambil_permissions(profile.id);
})
.then((permissions) => {
console.log("β
Permissions loaded:", permissions.join(", "));
console.log("π Semua data berhasil dimuat!");
})
.catch((error) => {
console.log("β Error dalam proses:", error);
});
Async/Await - Syntax Modern
Async/await membuat kode async terlihat seperti kode sync.
1. Async Function
async function proses_login_modern() {
try {
console.log("π Memulai proses login...");
// Menunggu hasil login
let user = await cek_login("admin", "123456");
console.log("β
Login berhasil:", user.username);
// Menunggu profile
let profile = await ambil_profile(user.id);
console.log("β
Profile loaded:", profile.nama_lengkap);
// Menunggu permissions
let permissions = await ambil_permissions(profile.id);
console.log("β
Permissions loaded:", permissions.join(", "));
console.log("π Semua proses selesai!");
return { user, profile, permissions };
} catch (error) {
console.log("β Error:", error);
throw error; // Re-throw error jika perlu
}
}
// Panggil async function
proses_login_modern()
.then((result) => {
console.log("π Hasil lengkap:", result);
})
.catch((error) => {
console.log("π« Proses login gagal total");
});
2. Multiple Async Operations
async function ambil_data_dashboard() {
try {
console.log("π Loading dashboard data...");
// Jalankan beberapa operasi secara paralel
let [cuaca, user_stats, notifikasi] = await Promise.all([
ambil_cuaca("Jakarta"),
ambil_user_statistics(),
ambil_notifikasi(),
]);
console.log("β
Semua data dashboard ready:");
console.log("Cuaca:", cuaca.suhu + "Β°C");
console.log("Total users:", user_stats.total_users);
console.log("Notifikasi baru:", notifikasi.length);
return { cuaca, user_stats, notifikasi };
} catch (error) {
console.log("β Gagal load dashboard:", error);
}
}
// Helper functions
function ambil_user_statistics() {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
total_users: 1250,
active_today: 89,
new_registrations: 12,
});
}, 1200);
});
}
function ambil_notifikasi() {
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{ id: 1, message: "User baru mendaftar", time: "5 menit lalu" },
{ id: 2, message: "Server maintenance", time: "1 jam lalu" },
{ id: 3, message: "Backup completed", time: "2 jam lalu" },
]);
}, 900);
});
}
// Test dashboard
ambil_data_dashboard();
Fetch API - HTTP Requests
Fetch adalah API modern untuk mengambil data dari server.
1. GET Request
async function ambil_data_posts() {
try {
console.log("π‘ Fetching posts from API...");
// Fetch data dari API
let response = await fetch(
"https://jsonplaceholder.typicode.com/posts?_limit=5"
);
// Cek apakah request berhasil
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
// Convert response ke JSON
let posts = await response.json();
console.log("β
Data berhasil diambil:");
posts.forEach((post, index) => {
console.log(`${index + 1}. ${post.title}`);
console.log(` ${post.body.substring(0, 50)}...`);
console.log("---");
});
return posts;
} catch (error) {
console.log("β Error fetching data:", error.message);
}
}
// Test fetch
ambil_data_posts();
2. POST Request
async function buat_post_baru(title, content, user_id) {
try {
console.log("π Membuat post baru...");
let response = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: title,
body: content,
userId: user_id,
}),
});
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
let new_post = await response.json();
console.log("β
Post berhasil dibuat:");
console.log("ID:", new_post.id);
console.log("Title:", new_post.title);
console.log("User ID:", new_post.userId);
return new_post;
} catch (error) {
console.log("β Error creating post:", error.message);
}
}
// Test POST
buat_post_baru(
"Tutorial JavaScript Async",
"Belajar async/await dengan mudah dan menyenangkan",
1
);
3. Fetch dengan Error Handling
async function fetch_dengan_retry(url, max_retries = 3) {
for (let attempt = 1; attempt <= max_retries; attempt++) {
try {
console.log(`π Percobaan ${attempt}/${max_retries}: ${url}`);
let response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
let data = await response.json();
console.log(`β
Berhasil pada percobaan ${attempt}`);
return data;
} catch (error) {
console.log(`β Percobaan ${attempt} gagal:`, error.message);
if (attempt === max_retries) {
console.log("π« Semua percobaan gagal");
throw error;
}
// Wait sebelum retry
await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
}
}
}
// Test retry mechanism
fetch_dengan_retry("https://jsonplaceholder.typicode.com/posts/1")
.then((data) => console.log("π Data berhasil diambil:", data.title))
.catch((error) => console.log("π₯ Gagal total:", error.message));
Contoh Praktis: Weather App
class WeatherApp {
constructor() {
this.api_key = "demo_key"; // Ganti dengan API key asli
this.base_url = "https://api.openweathermap.org/data/2.5";
}
async ambil_cuaca_kota(nama_kota) {
try {
console.log(`π€οΈ Mengambil cuaca ${nama_kota}...`);
// Simulasi API call (karena kita tidak punya API key asli)
await this.delay(1500);
// Simulasi data cuaca
let cuaca_data = {
kota: nama_kota,
negara: "ID",
suhu: Math.floor(Math.random() * 10) + 25, // 25-35Β°C
kondisi: this.random_kondisi(),
kelembaban: Math.floor(Math.random() * 30) + 60, // 60-90%
angin: Math.floor(Math.random() * 15) + 5, // 5-20 km/h
update_time: new Date().toLocaleString(),
};
return cuaca_data;
} catch (error) {
throw new Error(`Gagal mengambil cuaca: ${error.message}`);
}
}
async ambil_forecast_5_hari(nama_kota) {
try {
console.log(`π
Mengambil forecast 5 hari ${nama_kota}...`);
await this.delay(2000);
let forecast = [];
for (let i = 1; i <= 5; i++) {
let tanggal = new Date();
tanggal.setDate(tanggal.getDate() + i);
forecast.push({
tanggal: tanggal.toLocaleDateString(),
suhu_max: Math.floor(Math.random() * 8) + 28,
suhu_min: Math.floor(Math.random() * 5) + 22,
kondisi: this.random_kondisi(),
});
}
return forecast;
} catch (error) {
throw new Error(`Gagal mengambil forecast: ${error.message}`);
}
}
async tampilkan_info_lengkap(nama_kota) {
try {
console.log(`π Memuat info cuaca lengkap untuk ${nama_kota}...\n`);
// Ambil data cuaca dan forecast secara paralel
let [cuaca_sekarang, forecast] = await Promise.all([
this.ambil_cuaca_kota(nama_kota),
this.ambil_forecast_5_hari(nama_kota),
]);
// Tampilkan cuaca sekarang
console.log("=== CUACA HARI INI ===");
console.log(
`π Lokasi: ${cuaca_sekarang.kota}, ${cuaca_sekarang.negara}`
);
console.log(`π‘οΈ Suhu: ${cuaca_sekarang.suhu}Β°C`);
console.log(`βοΈ Kondisi: ${cuaca_sekarang.kondisi}`);
console.log(`π§ Kelembaban: ${cuaca_sekarang.kelembaban}%`);
console.log(`π¨ Angin: ${cuaca_sekarang.angin} km/h`);
console.log(`π Update: ${cuaca_sekarang.update_time}\n`);
// Tampilkan forecast
console.log("=== FORECAST 5 HARI ===");
forecast.forEach((hari, index) => {
console.log(`${index + 1}. ${hari.tanggal}`);
console.log(
` ${hari.suhu_min}Β°C - ${hari.suhu_max}Β°C, ${hari.kondisi}`
);
});
return { cuaca_sekarang, forecast };
} catch (error) {
console.log("β Error:", error.message);
}
}
// Helper methods
delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
random_kondisi() {
let kondisi_list = ["Cerah", "Berawan", "Hujan Ringan", "Mendung", "Panas"];
return kondisi_list[Math.floor(Math.random() * kondisi_list.length)];
}
}
// Test Weather App
let weather_app = new WeatherApp();
async function test_weather_app() {
await weather_app.tampilkan_info_lengkap("Jakarta");
console.log("\n" + "=".repeat(50) + "\n");
await weather_app.tampilkan_info_lengkap("Bandung");
}
test_weather_app();
Error Handling Best Practices
class APIClient {
constructor(base_url) {
this.base_url = base_url;
this.timeout = 5000; // 5 detik timeout
}
async request(endpoint, options = {}) {
try {
// Tambahkan timeout
let controller = new AbortController();
let timeout_id = setTimeout(() => controller.abort(), this.timeout);
let response = await fetch(this.base_url + endpoint, {
...options,
signal: controller.signal,
});
clearTimeout(timeout_id);
// Handle HTTP errors
if (!response.ok) {
throw new APIError(`HTTP ${response.status}`, response.status);
}
return await response.json();
} catch (error) {
if (error.name === "AbortError") {
throw new APIError("Request timeout", "TIMEOUT");
}
if (error instanceof APIError) {
throw error;
}
throw new APIError("Network error", "NETWORK_ERROR");
}
}
async get(endpoint) {
return this.request(endpoint, { method: "GET" });
}
async post(endpoint, data) {
return this.request(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
}
}
// Custom Error class
class APIError extends Error {
constructor(message, code) {
super(message);
this.name = "APIError";
this.code = code;
}
}
// Penggunaan dengan proper error handling
async function demo_error_handling() {
let api = new APIClient("https://jsonplaceholder.typicode.com");
try {
let posts = await api.get("/posts?_limit=3");
console.log("β
Posts berhasil diambil:", posts.length, "items");
} catch (error) {
if (error instanceof APIError) {
switch (error.code) {
case "TIMEOUT":
console.log("β° Request timeout - coba lagi");
break;
case "NETWORK_ERROR":
console.log("π Masalah koneksi internet");
break;
default:
console.log("π« API Error:", error.message);
}
} else {
console.log("π₯ Unexpected error:", error.message);
}
}
}
demo_error_handling();
Tips Async JavaScript
π‘ Selalu gunakan try-catch:
// β Tanpa error handling
async function bad_example() {
let data = await fetch("/api/data");
return data.json(); // Bisa error!
}
// β
Dengan error handling
async function good_example() {
try {
let response = await fetch("/api/data");
if (!response.ok) throw new Error("API Error");
return await response.json();
} catch (error) {
console.log("Error:", error.message);
return null;
}
}
π‘ Gunakan Promise.all untuk operasi paralel:
// β Sequential (lambat)
async function sequential() {
let user = await fetch_user(); // 1 detik
let posts = await fetch_posts(); // 1 detik
let comments = await fetch_comments(); // 1 detik
// Total: 3 detik
}
// β
Parallel (cepat)
async function parallel() {
let [user, posts, comments] = await Promise.all([
fetch_user(), // Semua jalan bersamaan
fetch_posts(),
fetch_comments(),
]);
// Total: 1 detik
}
Ringkasan
Async JavaScript untuk:
- Operasi yang membutuhkan waktu
- Network requests (API calls)
- File operations
- Timer/delay operations
3 Cara handle async:
- Callback - cara lama, bisa callback hell
- Promise - modern, dengan .then()/.catch()
- Async/Await - paling modern, mudah dibaca
Fetch API untuk:
- HTTP requests ke server
- GET, POST, PUT, DELETE data
- RESTful API communication
Best practices:
- Selalu handle errors
- Gunakan timeout untuk requests
- Parallel execution untuk performance
Siap eksplorasi ES6+ Features? Let's go modern! π