Pelajari cara memanfaatkan JavaScript Module Worker Threads untuk mencapai pemrosesan paralel, meningkatkan kinerja aplikasi, dan membuat aplikasi web serta Node.js yang lebih responsif. Panduan komprehensif untuk pengembang di seluruh dunia.
JavaScript Module Worker Threads: Memanfaatkan Pemrosesan Paralel untuk Peningkatan Kinerja
Dalam lanskap pengembangan web dan aplikasi yang terus berkembang, permintaan akan aplikasi yang lebih cepat, lebih responsif, dan efisien terus meningkat. Salah satu teknik kunci untuk mencapainya adalah melalui pemrosesan paralel, yang memungkinkan tugas-tugas dieksekusi secara bersamaan (konkuren) daripada secara berurutan. JavaScript, yang secara tradisional bersifat single-threaded, menawarkan mekanisme yang kuat untuk eksekusi paralel: Module Worker Threads.
Memahami Keterbatasan JavaScript Single-Threaded
JavaScript, pada intinya, bersifat single-threaded. Ini berarti bahwa secara default, kode JavaScript dieksekusi baris per baris, dalam satu alur eksekusi (thread). Meskipun kesederhanaan ini membuat JavaScript relatif mudah dipelajari dan dipahami, hal ini juga menimbulkan keterbatasan yang signifikan, terutama saat menangani tugas-tugas yang intensif secara komputasi atau operasi yang terikat I/O. Ketika tugas yang berjalan lama memblokir thread utama, hal itu dapat menyebabkan:
- UI Membeku (Freezing): Antarmuka pengguna menjadi tidak responsif, yang mengarah pada pengalaman pengguna yang buruk. Klik, animasi, dan interaksi lainnya menjadi tertunda atau diabaikan.
- Hambatan Kinerja (Bottlenecks): Kalkulasi kompleks, pemrosesan data, atau permintaan jaringan dapat secara signifikan memperlambat aplikasi.
- Responsivitas Menurun: Aplikasi terasa lamban dan tidak memiliki kelancaran yang diharapkan pada aplikasi web modern.
Bayangkan seorang pengguna di Tokyo, Jepang, berinteraksi dengan aplikasi yang sedang melakukan pemrosesan gambar yang kompleks. Jika pemrosesan tersebut memblokir thread utama, pengguna akan mengalami kelambatan yang signifikan, membuat aplikasi terasa lambat dan membuat frustrasi. Ini adalah masalah global yang dihadapi oleh pengguna di mana pun.
Memperkenalkan Module Worker Threads: Solusi untuk Eksekusi Paralel
Module Worker Threads menyediakan cara untuk memindahkan tugas-tugas yang intensif secara komputasi dari thread utama ke worker thread yang terpisah. Setiap worker thread mengeksekusi kode JavaScript secara independen, memungkinkan eksekusi paralel. Ini secara dramatis meningkatkan responsivitas dan kinerja aplikasi. Module Worker Threads adalah evolusi dari API Web Workers yang lebih lama, yang menawarkan beberapa keuntungan:
- Modularitas: Worker dapat dengan mudah diorganisir ke dalam modul menggunakan pernyataan `import` dan `export`, yang mempromosikan penggunaan kembali kode dan kemudahan pemeliharaan.
- Standar JavaScript Modern: Mengadopsi fitur-fitur ECMAScript terbaru, termasuk modul, membuat kode lebih mudah dibaca dan efisien.
- Kompatibilitas Node.js: Secara signifikan memperluas kemampuan pemrosesan paralel di lingkungan Node.js.
Pada dasarnya, worker thread memungkinkan aplikasi JavaScript Anda untuk memanfaatkan beberapa inti CPU, yang memungkinkan paralelisme sejati. Anggap saja seperti memiliki beberapa koki di dapur (thread) yang masing-masing mengerjakan hidangan yang berbeda (tugas) secara bersamaan, sehingga menghasilkan persiapan makanan (eksekusi aplikasi) yang lebih cepat secara keseluruhan.
Menyiapkan dan Menggunakan Module Worker Threads: Panduan Praktis
Mari kita selami cara menggunakan Module Worker Threads. Ini akan mencakup lingkungan browser dan lingkungan Node.js. Kami akan menggunakan contoh-contoh praktis untuk mengilustrasikan konsepnya.
Lingkungan Browser
Dalam konteks browser, Anda membuat worker dengan menentukan path ke file JavaScript yang berisi kode worker. File ini akan dieksekusi dalam thread terpisah.
1. Membuat Skrip Worker (worker.js):
// worker.js
import { parentMessage, calculateResult } from './utils.js';
self.onmessage = (event) => {
const { data } = event;
const result = calculateResult(data.number);
self.postMessage({ result });
};
2. Membuat Skrip Utilitas (utils.js):
export const parentMessage = "Message from parent";
export function calculateResult(number) {
// Mensimulasikan tugas yang intensif secara komputasi
let result = 0;
for (let i = 0; i < number; i++) {
result += Math.sqrt(i);
}
return result;
}
3. Menggunakan Worker di Skrip Utama Anda (main.js):
// main.js
const worker = new Worker('worker.js', { type: 'module' });
worker.onmessage = (event) => {
console.log('Hasil dari worker:', event.data.result);
// Perbarui UI dengan hasilnya
};
worker.onerror = (error) => {
console.error('Kesalahan worker:', error);
};
function startCalculation(number) {
worker.postMessage({ number }); // Kirim data ke worker
}
// Contoh: Mulai perhitungan saat tombol diklik
const button = document.getElementById('calculateButton'); // Anggap Anda memiliki tombol di HTML Anda
if (button) {
button.addEventListener('click', () => {
const input = document.getElementById('numberInput');
const number = parseInt(input.value, 10);
if (!isNaN(number)) {
startCalculation(number);
}
});
}
4. HTML (index.html):
<!DOCTYPE html>
<html>
<head>
<title>Contoh Worker</title>
</head>
<body>
<input type="number" id="numberInput" placeholder="Masukkan angka">
<button id="calculateButton">Hitung</button>
<script type="module" src="main.js"></script>
</body>
</html>
Penjelasan:
- worker.js: Di sinilah pekerjaan berat dilakukan. Event listener `onmessage` menerima data dari thread utama, melakukan perhitungan menggunakan `calculateResult`, dan mengirimkan hasilnya kembali ke thread utama menggunakan `postMessage()`. Perhatikan penggunaan `self` alih-alih `window` untuk merujuk ke lingkup global di dalam worker.
- main.js: Membuat instance worker baru. Metode `postMessage()` mengirimkan data ke worker, dan `onmessage` menerima data kembali dari worker. Event handler `onerror` sangat penting untuk men-debug kesalahan apa pun di dalam worker thread.
- HTML: Menyediakan antarmuka pengguna sederhana untuk memasukkan angka dan memicu perhitungan.
Pertimbangan Kunci di Browser:
- Batasan Keamanan: Worker berjalan dalam konteks terpisah dan tidak dapat mengakses DOM (Document Object Model) dari thread utama secara langsung. Komunikasi terjadi melalui pengiriman pesan (message passing). Ini adalah fitur keamanan.
- Transfer Data: Saat mengirim data ke dan dari worker, data biasanya diserialisasi dan dideserialisasi. Waspadai overhead yang terkait dengan transfer data dalam jumlah besar. Pertimbangkan untuk menggunakan `structuredClone()` untuk mengkloning objek guna menghindari mutasi data.
- Kompatibilitas Browser: Meskipun Module Worker Threads didukung secara luas, selalu periksa kompatibilitas browser. Gunakan deteksi fitur untuk menangani skenario di mana fitur tersebut tidak didukung dengan baik.
Lingkungan Node.js
Node.js juga mendukung Module Worker Threads, menawarkan kemampuan pemrosesan paralel dalam aplikasi sisi server. Ini sangat berguna untuk tugas-tugas yang terikat pada CPU seperti pemrosesan gambar, analisis data, atau menangani sejumlah besar permintaan secara bersamaan.
1. Membuat Skrip Worker (worker.mjs):
// worker.mjs
import { parentMessage, calculateResult } from './utils.mjs';
import { parentPort, isMainThread } from 'node:worker_threads';
if (!isMainThread) {
parentPort.on('message', (data) => {
const result = calculateResult(data.number);
parentPort.postMessage({ result });
});
}
2. Membuat Skrip Utilitas (utils.mjs):
export const parentMessage = "Pesan dari induk di node.js";
export function calculateResult(number) {
// Mensimulasikan tugas yang intensif secara komputasi
let result = 0;
for (let i = 0; i < number; i++) {
result += Math.sqrt(i);
}
return result;
}
3. Menggunakan Worker di Skrip Utama Anda (main.mjs):
// main.mjs
import { Worker, isMainThread } from 'node:worker_threads';
import { pathToFileURL } from 'node:url';
async function startWorker(number) {
return new Promise((resolve, reject) => {
const worker = new Worker(pathToFileURL('./worker.mjs').href, { type: 'module' });
worker.on('message', (result) => {
console.log('Hasil dari worker:', result.result);
resolve(result);
worker.terminate();
});
worker.on('error', (err) => {
console.error('Kesalahan worker:', err);
reject(err);
});
worker.on('exit', (code) => {
if (code !== 0) {
console.error(`Worker berhenti dengan kode keluar ${code}`);
reject(new Error(`Worker berhenti dengan kode keluar ${code}`));
}
});
worker.postMessage({ number }); // Kirim data ke worker
});
}
async function main() {
if (isMainThread) {
const result = await startWorker(10000000); // Kirim angka besar ke worker untuk dihitung.
console.log("Perhitungan selesai di thread utama.")
}
}
main();
Penjelasan:
- worker.mjs: Mirip dengan contoh browser, skrip ini berisi kode yang akan dieksekusi di worker thread. Skrip ini menggunakan `parentPort` untuk berkomunikasi dengan thread utama. `isMainThread` diimpor dari 'node:worker_threads' untuk memastikan skrip worker hanya dieksekusi saat tidak berjalan sebagai thread utama.
- main.mjs: Skrip ini membuat instance worker baru dan mengirimkan data kepadanya menggunakan `worker.postMessage()`. Skrip ini mendengarkan pesan dari worker menggunakan event `'message'` dan menangani kesalahan serta keluarnya worker. Metode `terminate()` digunakan untuk menghentikan worker thread setelah komputasi selesai, sehingga membebaskan sumber daya. Metode `pathToFileURL()` memastikan path file yang benar untuk impor worker.
Pertimbangan Kunci di Node.js:
- Path File: Pastikan path ke skrip worker dan modul yang diimpor sudah benar. Gunakan `pathToFileURL()` untuk resolusi path yang andal.
- Penanganan Kesalahan: Terapkan penanganan kesalahan yang kuat untuk menangkap pengecualian apa pun yang mungkin terjadi di worker thread. Event listener `worker.on('error', ...)` dan `worker.on('exit', ...)` sangat penting.
- Manajemen Sumber Daya: Hentikan worker thread ketika tidak lagi diperlukan untuk membebaskan sumber daya sistem. Kelalaian dalam melakukannya dapat menyebabkan kebocoran memori atau penurunan kinerja.
- Transfer Data: Pertimbangan yang sama tentang transfer data (overhead serialisasi) di browser juga berlaku untuk Node.js.
Manfaat Menggunakan Module Worker Threads
Manfaat menggunakan Module Worker Threads sangat banyak dan memiliki dampak signifikan pada pengalaman pengguna dan kinerja aplikasi:
- Peningkatan Responsivitas: Thread utama tetap responsif, bahkan ketika tugas-tugas yang intensif secara komputasi berjalan di latar belakang. Hal ini mengarah pada pengalaman pengguna yang lebih lancar dan menarik. Bayangkan seorang pengguna di Mumbai, India, berinteraksi dengan aplikasi. Dengan worker thread, pengguna tidak akan mengalami pembekuan yang membuat frustrasi saat perhitungan kompleks sedang dilakukan.
- Peningkatan Kinerja: Eksekusi paralel memanfaatkan beberapa inti CPU, memungkinkan penyelesaian tugas yang lebih cepat. Hal ini terutama terlihat pada aplikasi yang memproses kumpulan data besar, melakukan perhitungan kompleks, atau menangani banyak permintaan secara bersamaan.
- Peningkatan Skalabilitas: Dengan memindahkan pekerjaan ke worker thread, aplikasi dapat menangani lebih banyak pengguna dan permintaan secara bersamaan tanpa menurunkan kinerja. Ini sangat penting bagi bisnis di seluruh dunia dengan jangkauan global.
- Pengalaman Pengguna yang Lebih Baik: Aplikasi responsif yang memberikan umpan balik cepat terhadap tindakan pengguna akan menghasilkan kepuasan pengguna yang lebih besar. Hal ini berarti keterlibatan yang lebih tinggi dan, pada akhirnya, kesuksesan bisnis.
- Organisasi & Keterpeliharaan Kode: Module worker mempromosikan modularitas. Anda dapat dengan mudah menggunakan kembali kode di antara para worker.
Teknik dan Pertimbangan Tingkat Lanjut
Di luar penggunaan dasar, beberapa teknik tingkat lanjut dapat membantu Anda memaksimalkan manfaat dari Module Worker Threads:
1. Berbagi Data Antar Thread
Mengkomunikasikan data antara thread utama dan worker thread melibatkan metode `postMessage()`. Untuk struktur data yang kompleks, pertimbangkan:
- Structured Cloning: `structuredClone()` membuat salinan dalam (deep copy) dari sebuah objek untuk transfer. Hal ini menghindari masalah mutasi data yang tidak terduga di kedua thread.
- Transferable Objects: Untuk transfer data yang lebih besar (misalnya, `ArrayBuffer`), Anda dapat menggunakan objek yang dapat ditransfer (transferable objects). Ini mentransfer kepemilikan data yang mendasarinya ke worker, menghindari overhead penyalinan. Objek tersebut menjadi tidak dapat digunakan di thread asli setelah ditransfer.
Contoh penggunaan transferable objects:
// Thread utama
const buffer = new ArrayBuffer(1024);
const worker = new Worker('worker.js', { type: 'module' });
worker.postMessage({ buffer }, [buffer]); // Mentransfer kepemilikan buffer
// Worker thread (worker.js)
self.onmessage = (event) => {
const { buffer } = event.data;
// Akses dan bekerja dengan buffer
};
2. Mengelola Kumpulan Worker (Worker Pools)
Membuat dan menghancurkan worker thread secara sering bisa jadi mahal. Untuk tugas-tugas yang memerlukan penggunaan worker secara berkala, pertimbangkan untuk menerapkan kumpulan worker (worker pool). Kumpulan worker memelihara satu set worker thread yang sudah dibuat sebelumnya yang dapat digunakan kembali untuk mengeksekusi tugas. Hal ini mengurangi overhead pembuatan dan penghancuran thread, sehingga meningkatkan kinerja.
Implementasi konseptual dari kumpulan worker:
class WorkerPool {
constructor(workerFile, numberOfWorkers) {
this.workerFile = workerFile;
this.numberOfWorkers = numberOfWorkers;
this.workers = [];
this.queue = [];
this.initializeWorkers();
}
initializeWorkers() {
for (let i = 0; i < this.numberOfWorkers; i++) {
const worker = new Worker(this.workerFile, { type: 'module' });
worker.onmessage = (event) => {
const task = this.queue.shift();
if (task) {
task.resolve(event.data);
}
// Opsional, tambahkan kembali worker ke antrean 'bebas'
// atau biarkan worker tetap aktif untuk tugas berikutnya secara langsung.
};
worker.onerror = (error) => {
console.error('Kesalahan worker:', error);
// Tangani kesalahan dan berpotensi memulai ulang worker
};
this.workers.push(worker);
}
}
async execute(data) {
return new Promise((resolve, reject) => {
this.queue.push({ resolve, reject });
const worker = this.workers.shift(); // Dapatkan worker dari kumpulan (atau buat satu)
if (worker) {
worker.postMessage(data);
this.workers.push(worker); // Masukkan kembali worker ke dalam antrean.
} else {
// Tangani kasus di mana tidak ada worker yang tersedia.
reject(new Error('Tidak ada worker yang tersedia di kumpulan.'));
}
});
}
terminate() {
this.workers.forEach(worker => worker.terminate());
}
}
// Contoh Penggunaan:
const workerPool = new WorkerPool('worker.js', 4); // Buat kumpulan 4 worker
async function processData() {
const result = await workerPool.execute({ task: 'someData' });
console.log(result);
}
3. Penanganan Kesalahan dan Debugging
Debugging worker thread bisa lebih menantang daripada men-debug kode single-threaded. Berikut beberapa tips:
- Gunakan Event `onerror` dan `error`: Lampirkan event listener `onerror` ke instance worker Anda untuk menangkap kesalahan dari worker thread. Di Node.js, gunakan event `error`.
- Logging: Gunakan `console.log` dan `console.error` secara ekstensif baik di thread utama maupun di worker thread. Pastikan log dibedakan dengan jelas untuk mengidentifikasi thread mana yang menghasilkannya.
- Browser Developer Tools: Alat pengembang browser (misalnya, Chrome DevTools, Firefox Developer Tools) menyediakan kemampuan debugging untuk web worker. Anda dapat mengatur breakpoint, memeriksa variabel, dan menelusuri kode baris per baris.
- Debugging Node.js: Node.js menyediakan alat debugging (misalnya, menggunakan flag `--inspect`) untuk men-debug worker thread.
- Uji Secara Menyeluruh: Uji aplikasi Anda secara menyeluruh, terutama di berbagai browser dan sistem operasi. Pengujian sangat penting dalam konteks global untuk memastikan fungsionalitas di berbagai lingkungan.
4. Menghindari Kesalahan Umum
- Deadlock: Pastikan worker Anda tidak terblokir menunggu satu sama lain (atau thread utama) untuk melepaskan sumber daya, yang menciptakan situasi deadlock. Rancang alur tugas Anda dengan hati-hati untuk mencegah skenario semacam itu.
- Overhead Serialisasi Data: Minimalkan jumlah data yang Anda transfer antar thread. Gunakan transferable objects jika memungkinkan, dan pertimbangkan untuk mengelompokkan data (batching) untuk mengurangi jumlah panggilan `postMessage()`.
- Konsumsi Sumber Daya: Pantau penggunaan sumber daya worker (CPU, memori) untuk mencegah worker thread mengonsumsi sumber daya yang berlebihan. Terapkan batas sumber daya yang sesuai atau strategi penghentian jika diperlukan.
- Kompleksitas: Sadarilah bahwa memperkenalkan pemrosesan paralel akan meningkatkan kompleksitas kode Anda. Rancang worker Anda dengan tujuan yang jelas dan jaga agar komunikasi antar thread sesederhana mungkin.
Kasus Penggunaan dan Contoh
Module Worker Threads dapat diterapkan dalam berbagai skenario. Berikut adalah beberapa contoh yang menonjol:
- Pemrosesan Gambar: Pindahkan pengubahan ukuran gambar, pemfilteran, dan manipulasi gambar kompleks lainnya ke worker thread. Ini menjaga antarmuka pengguna tetap responsif sementara pemrosesan gambar terjadi di latar belakang. Bayangkan sebuah platform berbagi foto yang digunakan secara global. Ini akan memungkinkan pengguna di Rio de Janeiro, Brasil, dan London, Inggris, untuk mengunggah dan memproses foto dengan cepat tanpa ada pembekuan UI.
- Pemrosesan Video: Lakukan pengkodean, dekode, dan tugas-tugas terkait video lainnya di worker thread. Ini memungkinkan pengguna untuk terus menggunakan aplikasi saat pemrosesan video sedang berlangsung.
- Analisis Data dan Perhitungan: Pindahkan analisis data yang intensif secara komputasi, perhitungan ilmiah, dan tugas-tugas machine learning ke worker thread. Ini meningkatkan responsivitas aplikasi, terutama saat bekerja dengan kumpulan data yang besar.
- Pengembangan Game: Jalankan logika game, AI, dan simulasi fisika di worker thread, memastikan gameplay yang lancar bahkan dengan mekanika game yang kompleks. Sebuah game online multipemain populer yang dapat diakses dari Seoul, Korea Selatan, perlu memastikan lag minimal bagi para pemain. Hal ini dapat dicapai dengan memindahkan perhitungan fisika.
- Permintaan Jaringan: Untuk beberapa aplikasi, Anda dapat menggunakan worker untuk menangani beberapa permintaan jaringan secara bersamaan, meningkatkan kinerja aplikasi secara keseluruhan. Namun, waspadai batasan worker thread terkait pembuatan permintaan jaringan langsung.
- Sinkronisasi Latar Belakang: Sinkronkan data dengan server di latar belakang tanpa memblokir thread utama. Ini berguna untuk aplikasi yang memerlukan fungsionalitas offline atau yang perlu memperbarui data secara berkala. Aplikasi seluler yang digunakan di Lagos, Nigeria, yang secara berkala menyinkronkan data dengan server akan sangat diuntungkan dari worker thread.
- Pemrosesan File Besar: Proses file besar dalam potongan-potongan menggunakan worker thread untuk menghindari pemblokiran thread utama. Ini sangat berguna untuk tugas-tugas seperti unggahan video, impor data, atau konversi file.
Praktik Terbaik untuk Pengembangan Global dengan Module Worker Threads
Saat mengembangkan dengan Module Worker Threads untuk audiens global, pertimbangkan praktik terbaik berikut:
- Kompatibilitas Lintas-Browser: Uji kode Anda secara menyeluruh di berbagai browser dan perangkat yang berbeda untuk memastikan kompatibilitas. Ingatlah bahwa web diakses melalui beragam browser, dari Chrome di Amerika Serikat hingga Firefox di Jerman.
- Optimisasi Kinerja: Optimalkan kode Anda untuk kinerja. Minimalkan ukuran skrip worker Anda, kurangi overhead transfer data, dan gunakan algoritma yang efisien. Ini memengaruhi pengalaman pengguna dari Toronto, Kanada, hingga Sydney, Australia.
- Aksesibilitas: Pastikan aplikasi Anda dapat diakses oleh pengguna dengan disabilitas. Sediakan teks alternatif untuk gambar, gunakan HTML semantik, dan ikuti pedoman aksesibilitas. Ini berlaku untuk pengguna dari semua negara.
- Internasionalisasi (i18n) dan Lokalisasi (l10n): Pertimbangkan kebutuhan pengguna di berbagai wilayah. Terjemahkan aplikasi Anda ke dalam beberapa bahasa, sesuaikan antarmuka pengguna dengan budaya yang berbeda, dan gunakan format tanggal, waktu, dan mata uang yang sesuai.
- Pertimbangan Jaringan: Waspadai kondisi jaringan. Pengguna di area dengan koneksi internet yang lambat akan mengalami masalah kinerja secara lebih parah. Optimalkan aplikasi Anda untuk menangani latensi jaringan dan batasan bandwidth.
- Keamanan: Amankan aplikasi Anda dari kerentanan web yang umum. Lakukan sanitasi input pengguna, lindungi dari serangan cross-site scripting (XSS), dan gunakan HTTPS.
- Pengujian Lintas Zona Waktu: Lakukan pengujian di berbagai zona waktu untuk mengidentifikasi dan mengatasi masalah apa pun yang terkait dengan fitur yang sensitif terhadap waktu atau proses latar belakang.
- Dokumentasi: Sediakan dokumentasi, contoh, dan tutorial yang jelas dan ringkas dalam bahasa Inggris. Pertimbangkan untuk menyediakan terjemahan untuk adopsi yang lebih luas.
- Terapkan Pemrograman Asinkron: Module Worker Threads dibuat untuk operasi asinkron. Pastikan kode Anda secara efektif menggunakan `async/await`, Promises, dan pola asinkron lainnya untuk hasil terbaik. Ini adalah konsep dasar dalam JavaScript modern.
Kesimpulan: Merangkul Kekuatan Paralelisme
Module Worker Threads adalah alat yang ampuh untuk meningkatkan kinerja dan responsivitas aplikasi JavaScript. Dengan memungkinkan pemrosesan paralel, mereka memungkinkan pengembang untuk memindahkan tugas-tugas yang intensif secara komputasi dari thread utama, memastikan pengalaman pengguna yang lancar dan menarik. Dari pemrosesan gambar dan analisis data hingga pengembangan game dan sinkronisasi latar belakang, Module Worker Threads menawarkan banyak kasus penggunaan di berbagai aplikasi.
Dengan memahami dasar-dasarnya, menguasai teknik-teknik tingkat lanjut, dan mematuhi praktik terbaik, pengembang dapat memanfaatkan potensi penuh dari Module Worker Threads. Seiring pengembangan web dan aplikasi terus berkembang, merangkul kekuatan paralelisme melalui Module Worker Threads akan menjadi penting untuk membangun aplikasi yang berkinerja tinggi, dapat diskalakan, dan ramah pengguna yang memenuhi tuntutan audiens global. Ingat, tujuannya adalah untuk menciptakan aplikasi yang bekerja dengan lancar, di mana pun pengguna berada di planet ini – dari Buenos Aires, Argentina, hingga Beijing, Tiongkok.