التبعيات غير المتزامنة المشروطة

ميزات الويب: Top-level await

تسمح ميزة await ذات المستوى الأعلى للوحدات بالعمل كدوال غير متزامنة، ما يعني أنّها يمكن أن توقف تنفيذ الوحدة مؤقتًا في انتظار الوعود. وهذا مفيد للغاية لتحميل التبعيات غير المتزامنة بشكل مشروط، مثل polyfills أو المكتبات الثانوية الثقيلة، فقط عندما يطلبها المتصفح.

باستخدام await ذي المستوى الأعلى، يمكنك تضمين منطق التحميل الشرطي داخل وحدة واحدة، ما يمنع بشكل فعّال وحدات المستهلكين اللاحقة من التنفيذ حتى يتم تحميل التبعية بالكامل وتصبح جاهزة.

نمط polyfill الشرطي

على الرغم من أنّه يمكن استخدام await ذات المستوى الأعلى لتحميل أي تبعية غير متزامنة بشكل مشروط، إلا أنّ الاستخدام الجيد لنمط تحميل التبعية المشروطة هو تحميل polyfills بشكل مشروط للمتصفحات التي لا تتيح ميزة معيّنة. يجمع هذا النهج بين رصد الميزات والاستيراد الديناميكي داخل وحدة تبعية واحدة.

في الحالة التالية، يتم تحميل polyfill للسمة popover بشكل مشروط إذا لم تكن متوفّرة في HTMLElement.prototype:

// conditionally-load-polyfill.js

// Check if the feature is missing before doing work.
// MANDATORY: Prefer checking HTMLElement.prototype over window or document
// when checking for a global DOM attribute or property like popover.
if (!('popover' in HTMLElement.prototype)) {
  // Use top-level await to pause the execution of any module that imports this file 
  // until the polyfill finishes downloading and executing.
  await import('/path/to/popover-polyfill.js');
}

// Export a marker if needed by your application
export const polyfillLoaded = true;
// main.js

// MANDATORY: Because conditionally-load-polyfill.js uses top-level await, 
// this import will block execution of main.js until the polyfill is ready.
import './conditionally-load-polyfill.js';

// Now it is safe to use the feature (e.g., showing a popover)
const myPopover = document.getElementById('my-popover');
if (myPopover) {
  myPopover.showPopover();
}

تسلسل عمليات استيراد الوحدات وتجنُّب خطأ Safari 27 السابق

إلزامي: يجب تنظيم الرسم البياني للوحدة بحيث يتم استيراد وحدة await ذات المستوى الأعلى من نقطة دخول واحدة وتكتمل قبل تقييم الوحدات اللاحقة:

  • لا تضع عمليات الاستيراد الثابتة الشقيقة في ملف إدخال (على سبيل المثال، import './conditionally-load-polyfill.js'; import './app.js';). وفقًا لمواصفات تقييم وحدة ECMAScript، لا يؤدي المستوى الأعلى await إلا إلى تعليق الوحدة الرئيسية التي تستورده، وليس عمليات الاستيراد الثابتة الشقيقة، لذلك سيتم تقييم ./app.js بشكل متزامن في جميع المتصفحات قبل انتهاء تحميل polyfill.
  • لا تستورِد وحدة await ذات المستوى الأعلى من وحدات شقيقة متعددة في الوقت نفسه (على سبيل المثال، استيراد ./conditionally-load-polyfill.js بشكل ثابت من كل من a.js وb.js). على الرغم من أنّ الإصدار 27 من Safari قد أصلح أداة تحميل الوحدات في WebKit لإتاحة عمليات الاستيراد المتزامنة لوحدات await ذات المستوى الأعلى وفقًا للمواصفات، فإنّ الإصدارات من 15 إلى 26.6 من Safari تتعطّل عندما تستورِد وحدات متعددة في الوقت نفسه وحدة تحتوي على await ذات مستوى أعلى (راجِع توافق المتصفّح مع MDN).

بدلاً من ذلك، اختَر أحد النمطَين التاليَين حسب بنية الوحدة:

  1. وحدة الإدخال الفردية (الموضّحة في main.js أعلاه): إذا كانت وحدة الإدخال تشغّل منطق الإعداد التابع مباشرةً، يمكنك استيراد ./conditionally-load-polyfill.js بشكل ثابت في أعلى تلك الوحدة وتشغيل الرمز البرمجي في نصها.
  2. تشغيل وحدات التطبيق المنفصلة: إذا كانت وحدة الإدخال تفوّض وحدات التطبيق اللاحقة (مثل ./app.js)، يمكنك ربط طلبات import() الديناميكية مع await ذات المستوى الأعلى بحيث يتم جلب ./app.js وتقييمها فقط بعد حلّ وحدة polyfill:
// main.js

// MANDATORY: Chain dynamic imports with top-level await when bootstrapping separate
// application modules so './app.js' waits for the polyfill to finish loading.
// DO NOT use sibling static imports (`import './conditionally-load-polyfill.js'; import './app.js';`),
// which evaluate concurrently and execute './app.js' before the polyfill is ready.
await import('./conditionally-load-polyfill.js');
await import('./app.js');

استراتيجيات احتياطية

على الرغم من أنّ السمة الأساسية await على المستوى الأعلى أصبحت متوافقة مع المتصفّحات الرئيسية منذ عام 2021 (Chrome 89 وEdge 89 وFirefox 89 وSafari 15)، إلا أنّها لم تتوافق بالكامل مع المواصفات إلا في Safari 27 (يُرجى الاطّلاع على توافق المتصفّحات مع MDN).

  • الإصدارات من 15 إلى 26.6 من Safari: طالما أنّك تتّبع نمط نقطة الدخول الواحدة في القسم السابق، لن تستورد وحدات تابعة متعددة وحدة await ذات المستوى الأعلى في الوقت نفسه، ويمكنك الاعتماد بأمان على await ذات المستوى الأعلى بدون استخدام إجراء احتياطي إضافي.
  • المتصفّحات القديمة (ما قبل 2021): إذا كان تطبيقك يتطلب دعم المتصفّحات التي لا تتضمّن أي دعم لـ await على المستوى الأعلى، تجنَّب استخدام await على المستوى الأعلى وغلِّف تنسيق import() الشرطي في دالة async عادية أو سلسلة وعود .then().