من أكبر اللحظات في مسيرتي كمطوّرة ويب ثلاثي الأبعاد عندما فهمت أخيرًا Promise وasync / await وfetch وaxios وPromise.all. كانت هذه المفاهيم مربكة في البداية. لكن عندما بدأت بناء مشاريع حقيقية باستخدام Three.js وWebGL والمُهيّئات والمشاهد ثلاثية الأبعاد الكبيرة، أدركت: البرمجة غير المتزامنة موجودة في كل مكان في تطبيقات الويب ثلاثية الأبعاد الحديثة.
الخطوة 1 — لماذا نحتاج البرمجة غير المتزامنة؟
يعمل JavaScript سطرًا بسطر، لكن بعض العمليات تستغرق وقتًا: طلبات API، وتحميل النماذج ثلاثية الأبعاد، وتحميل الخامات، وتنزيل بيئات HDR، ورفع الملفات. لو انتظر JavaScript هذه المهام بشكل متزامن لتجمّدت الواجهة كلها. لهذا وُجدت البرمجة غير المتزامنة.
الخطوة 2 — ما هو 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 (مرفوض).
الخطوة 3 — 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.
الخطوة 4 — fetch
fetch() مدمجة في المتصفح وتُستخدم لطلبات HTTP:
async function getProducts(): Promise<void> {
const response = await fetch("/api/products");
const data = await response.json();
console.log(data);
}في المُهيّئات ثلاثية الأبعاد يمكن لهذه البيانات تغيير الخامات وتحديث القوام وتبديل النماذج وضبط نسخ المنتج.
الخطوة 5 — axios
يستخدم كثير من المطورين axios لصياغته الأنظف ومعالجة الأخطاء الأفضل والـ Interceptors وسهولة إدارة المصادقة:
import axios from "axios";
async function getMaterials(): Promise<void> {
const response = await axios.get("/api/materials");
console.log(response.data);
}الخطوة 6 — البرمجة غير المتزامنة في Three.js
في Three.js التحميل غير المتزامن في كل مكان: نماذج GLTF والخامات وملفات HDR والصوت والحركات. كانت المحمّلات القديمة تستخدم الاستدعاءات:
loader.load("/chair.glb", (gltf) => {
scene.add(gltf.scene);
});هذا يعمل، لكن الاستدعاءات تصبح فوضوية في المشاريع الكبيرة.
الخطوة 7 — loadAsync()
توفّر محمّلات Three.js الحديثة أيضًا loader.loadAsync() التي تعيد Promise — فنستطيع استخدام async/await بشكل طبيعي:
const gltf = await loader.loadAsync("/chair.glb");
scene.add(gltf.scene);أي: «انتظر حتى يكتمل تحميل النموذج».
الخطوة 8 — التحميل المتسلسل مقابل المتوازي
❌ بطيء (تحميل متسلسل):
const chair = await loader.loadAsync("/chair.glb");
const table = await loader.loadAsync("/table.glb");
const sofa = await loader.loadAsync("/sofa.glb");يُحمَّل الكرسي أولًا ثم الطاولة ثم الأريكة — وكل أصل ينتظر السابق.
الخطوة 9 — Promise.all()
تشغّل Promise.all() عدة Promises في الوقت نفسه.
✅ أسرع (تحميل متوازٍ):
const [chair, table, sofa] = await Promise.all([
loader.loadAsync("/chair.glb"),
loader.loadAsync("/table.glb"),
loader.loadAsync("/sofa.glb"),
]);الآن تُحمَّل كل النماذج بالتوازي، ما قد يحسّن أداء المُهيّئات والمتاجر الإلكترونية وتطبيقات الميتافيرس والتوائم الرقمية والمواقع ثلاثية الأبعاد التفاعلية بشكل كبير.
الخطوة 10 — معمارية 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 المعاصرين. ما النمط غير المتزامن الذي تستخدمه أكثر في مشاريعك ثلاثية الأبعاد؟



