استثمر حلقة الأحداث، ووازِ وعودك بشكل صحيح، واجعل معالجة الأخطاء غير المتزامنة موثوقة.
افتح هذا الدرس في Kodokonيعتمد نموذج التنفيذ على طابور مهام كبرى (setTimeout والدخل/الخرج) وطابور مهام صغرى (.then وawait وqueueMicrotask). بعد كل مهمة، يفرّغ المحرك طابور المهام الصغرى بالكامل قبل الانتقال. والنتيجة المباشرة: يعمل وعد محلول دائما قبل setTimeout(fn, 0)، وقد تجمّد سلسلة مهام صغرى مفرطة الطول عرض الصفحة تماما كحلقة متزامنة.
console.log("start");
setTimeout(() => console.log("macrotask"), 0);
Promise.resolve().then(() => console.log("microtask"));
console.log("end");
// start, end, microtask, macrotaskasync/await سكّر لغوي فوق الوعود، لكنه يخفي مزلق أداء: كل await داخل حلقة يسلسل العمليات. فإن كانت الطلبات مستقلة، فأطلقها جميعا أولا ثم انتظر الدفعة بـ Promise.all. فلثلاثة طلبات بزمن 100 مِلّي ثانية، تكلّف النسخة التسلسلية 300 مِلّي ثانية، والنسخة المتوازية 100 مِلّي ثانية.
const fetchUser = (id) => new Promise((res) => {
setTimeout(() => res({ id }), 100);
});
async function loadSequential(ids) {
const users = [];
for (const id of ids) {
users.push(await fetchUser(id)); // ~100 ms each
}
return users;
}
async function loadParallel(ids) {
return Promise.all(ids.map(fetchUser)); // ~100 ms total
}اختر المُركِّب الصحيح وفق عقد الفشل الذي تحتاجه: يرفض Promise.all عند أول فشل (الكل أو لا شيء)، وينتظر allSettled الجميع ويصف كل نتيجة، ويأخذ race أول تسوية (نجاحا كانت أم فشلا، مفيدة لمهلة زمنية)، ويأخذ any أول نجاح ولا يرفض إلا إذا فشل كل شيء. وللوحة قيادة يجب أن تعرض نتائج جزئية، يكون allSettled هو الخيار الصحيح دائما تقريبا.
const tasks = [
Promise.resolve("profile loaded"),
Promise.reject(new Error("network unavailable")),
];
const results = await Promise.allSettled(tasks);
for (const r of results) {
if (r.status === "fulfilled") {
console.log("success:", r.value);
} else {
console.error("failure:", r.reason.message);
}
}console.log متزامن، واستدعاء Promise.resolve().then، واستدعاء setTimeout(fn, 0)؟try { doAsync(); } catch (e) {} بلا await في التقاط الرفض من doAsync؟