Async/Await در TypeScript — با مثال‌های واقعی در Three.js

یکی از بزرگ‌ترین لحظه‌های مسیر من به عنوان توسعه‌دهنده وب سه‌بعدی، وقتی بود که بالاخره Promise، async / await، fetch، axios و Promise.all را فهمیدم. اول این مفاهیم گیج‌کننده بودند. اما وقتی پروژه‌های واقعی با Three.js، WebGL، کانفیگوراتورها و صحنه‌های سه‌بعدی بزرگ ساختم، فهمیدم: برنامه‌نویسی غیرهمزمان در اپلیکیشن‌های سه‌بعدی وب مدرن همه جا هست.

قدم ۱ — چرا به برنامه‌نویسی غیرهمزمان نیاز داریم؟

جاوااسکریپت خط به خط اجرا می‌شود، اما بعضی عملیات زمان می‌برند: درخواست‌های API، بارگذاری مدل سه‌بعدی، بارگذاری تکسچر، دانلود محیط‌های HDR، آپلود فایل. اگر جاوااسکریپت همزمان منتظر این کارها می‌ماند، کل رابط کاربری قفل می‌شد. به همین دلیل برنامه‌نویسی غیرهمزمان وجود دارد.

قدم ۲ — Promise چیست؟

Promise یعنی: «الان نتیجه را ندارم، اما بعداً به تو می‌دهم.»

const promise: Promise<string> = new Promise((resolve, reject) => {
  const success = true;
  if (success) {
    resolve("Model Loaded");
  } else {
    reject("Loading Error");
  }
});

Promise سه حالت دارد: pending (در انتظار)، fulfilled (انجام‌شده) و rejected (رد‌شده).

قدم ۳ — async / await

پیش از async/await، توسعه‌دهنده‌ها اغلب از زنجیره .then() استفاده می‌کردند که خواندنش سخت می‌شد:

loadModel()
  .then(loadTexture)
  .then(loadHDRI);

حالا می‌توانیم کد تمیزتری بنویسیم:

async function initScene(): Promise<void> {
  const model = await loadModel();
  const texture = await loadTexture();
}

await یعنی «صبر کن تا این Promise تمام شود». قانون مهم: await را فقط داخل تابع async می‌توانید استفاده کنید.

قدم ۴ — fetch

fetch() در خود مرورگر وجود دارد و برای درخواست‌های HTTP استفاده می‌شود:

async function getProducts(): Promise<void> {
  const response = await fetch("/api/products");
  const data = await response.json();
  console.log(data);
}

در کانفیگوراتورهای سه‌بعدی، این داده می‌تواند متریال‌ها را تغییر دهد، تکسچرها را به‌روز کند، مدل‌ها را عوض کند و نسخه‌های مختلف محصول را تنظیم کند.

قدم ۵ — axios

خیلی از توسعه‌دهنده‌ها به خاطر نحو تمیزتر، مدیریت خطای بهتر، Interceptorها و مدیریت ساده‌تر احراز هویت از axios استفاده می‌کنند:

import axios from "axios";

async function getMaterials(): Promise<void> {
  const response = await axios.get("/api/materials");
  console.log(response.data);
}

قدم ۶ — برنامه‌نویسی غیرهمزمان در Three.js

در Three.js بارگذاری غیرهمزمان همه جا هست: مدل‌های GLTF، تکسچرها، فایل‌های HDR، صدا و انیمیشن‌ها. لودرهای قدیمی‌تر از Callback استفاده می‌کردند:

loader.load("/chair.glb", (gltf) => {
  scene.add(gltf.scene);
});

این کار می‌کند، اما در پروژه‌های بزرگ Callbackها شلوغ می‌شوند.

قدم ۷ — loadAsync()

لودرهای مدرن Three.js متد loader.loadAsync() هم دارند که یک Promise برمی‌گرداند — پس می‌توانیم به طور طبیعی از async/await استفاده کنیم:

const gltf = await loader.loadAsync("/chair.glb");
scene.add(gltf.scene);

یعنی: «صبر کن تا مدل کامل بارگذاری شود».

قدم ۸ — بارگذاری ترتیبی در برابر موازی

❌ کند (بارگذاری ترتیبی):

const chair = await loader.loadAsync("/chair.glb");
const table = await loader.loadAsync("/table.glb");
const sofa = await loader.loadAsync("/sofa.glb");

اول صندلی بارگذاری می‌شود، بعد میز، بعد مبل — هر دارایی منتظر قبلی می‌ماند.

قدم ۹ — Promise.all()

Promise.all() چند Promise را هم‌زمان اجرا می‌کند.

✅ سریع‌تر (بارگذاری موازی):

const [chair, table, sofa] = await Promise.all([
  loader.loadAsync("/chair.glb"),
  loader.loadAsync("/table.glb"),
  loader.loadAsync("/sofa.glb"),
]);

حالا همه مدل‌ها موازی بارگذاری می‌شوند که می‌تواند پرفورمنس کانفیگوراتورها، فروشگاه‌های اینترنتی، اپ‌های متاورس، دوقلوهای دیجیتال و سایت‌های سه‌بعدی تعاملی را به‌شدت بهتر کند.

قدم ۱۰ — معماری واقعی در Three.js

در پروژه‌های واقعی معمولاً توابع لودر قابل استفاده دوباره می‌سازیم:

async function loadChair(): Promise<THREE.Group> {
  const gltf = await loader.loadAsync("/chair.glb");
  gltf.scene.scale.set(2, 2, 2);
  return gltf.scene;
}

const [chair, sofa] = await Promise.all([loadChair(), loadSofa()]);

این کار اپلیکیشن‌های بزرگ Three.js را تمیزتر، مقیاس‌پذیرتر و نگهداری‌پذیرتر می‌کند.

جمع‌بندی

فهم برنامه‌نویسی غیرهمزمان کاملاً شیوه ساخت اپلیکیشن‌های Three.js را برایم تغییر داد. وقتی واقعاً Promise، async/await، fetch، axios، loadAsync و Promise.all را بفهمید، کدتان تمیزتر، سریع‌تر، مقیاس‌پذیرتر و دیباگش ساده‌تر می‌شود.

برنامه‌نویسی غیرهمزمان دیگر برای توسعه‌دهندگان مدرن Three.js اختیاری نیست. شما در پروژه‌های سه‌بعدی‌تان بیشتر از کدام الگوی async استفاده می‌کنید؟