استخدام "global" في انتظار جافا سكريبت





الميزة الجديدة التي يمكن أن تغير الطريقة التي نكتب بها



JavaScript هي لغة مرنة وقوية للغاية تعمل على تشكيل تطور الويب الحديث. أحد الأسباب الرئيسية التي تجعل JavaScript مهيمنًا جدًا في تطوير الويب هو التطور السريع والتحسين المستمر.



اقتراح واحد لتحسين JavaScript هو الاقتراحيسمى "انتظار المستوى الأعلى" (انتظار المستوى الأعلى ، انتظار "عالمي"). الهدف من هذا الاقتراح هو تحويل وحدات ES إلى شيء مثل الوظائف غير المتزامنة. سيسمح ذلك للوحدات النمطية بالحصول على موارد جاهزة للاستخدام ومنع الوحدات النمطية من استيرادها. لن تتمكن الوحدات النمطية التي تستورد الموارد المتوقعة من تشغيل تنفيذ التعليمات البرمجية إلا بعد استلام الموارد وإعدادها للاستخدام.



يخضع هذا الاقتراح حاليًا للدراسة في 3 مراحل ، لذا لا يمكن استخدام هذه الميزة في الإنتاج. ومع ذلك ، يمكنك التأكد من أنه سيتم تنفيذه بالتأكيد في المستقبل القريب.



لا تقلق بشأن هذا. تابع القراءة. سأوضح لك كيف يمكنك استخدام الميزة المسماة الآن.



ما الخطأ في الانتظار العادي؟



إذا حاولت استخدام الكلمة الأساسية الانتظار خارج وظيفة غير متزامنة ، فستتلقى خطأً في بناء الجملة. لتجنب ذلك ، يستخدم المطورون التعبير الوظيفي المستدعي فورًا (IIFE).



await Promise.resolve(console.log("️")); // 

(async () => {
    await Promise.resolve(console.log("️"))
})();


المشكلة المحددة وحلها مجرد غيض من فيض




عند العمل مع وحدات ES6 ، فإنك تميل إلى التعامل مع الكثير من المثيلات التي تقوم بتصدير واستيراد القيم. لنفكر في مثال:



// library.js
export const sqrt = Math.sqrt;
export const square = (x) => x * x;
export const diagonal = (x, y) => sqrt((square(x) + square(y)));

// middleware.js
import { square, diagonal } from "./library.js";

console.log("From Middleware");

let squareOutput;
let diagonalOutput;

const delay = (ms) => new Promise((resolve) => {
    const timer = setTimeout(() => {
        resolve(console.log("️"));
        clearTimeout(timer);
    }, ms);
});

// IIFE
(async () => {
    await delay(1000);
    squareOutput = square(13);
    diagonalOutput = diagonal(12, 5);
})();

export { squareOutput, diagonalOutput };


في المثال أعلاه ، نقوم بتصدير واستيراد المتغيرات بين library.js و middleware.js. يمكنك تسمية الملفات كما تريد.



ترجع وظيفة التأخير وعدًا يتم حله بعد تأخير. نظرًا لأن هذه الوظيفة غير متزامنة ، فإننا نستخدم الكلمة الأساسية "انتظار" داخل IIFE "للانتظار" حتى تكتمل. في التطبيق الحقيقي ، بدلاً من وظيفة "التأخير" ، سيكون هناك استدعاء جلب أو مهمة أخرى غير متزامنة. بعد حل الوعد ، نسند القيمة إلى متغيرنا. هذا يعني أن المتغير الخاص بنا لن يتم تحديده حتى يتم حل الوعد.



في نهاية الكود ، نقوم بتصدير متغيراتنا بحيث يمكن استخدامها في كود آخر.



دعنا نلقي نظرة على الكود حيث يتم استيراد هذه المتغيرات واستخدامها:



// main.js
import { squareOutput, diagonalOutput } from "./middleware.js";

console.log(squareOutput); // undefined
console.log(diagonalOutput); // undefined
console.log("From Main");

const timer1 = setTimeout(() => {
    console.log(squareOutput);
    clearTimeout(timer1);
}, 2000); // 169

const timer2 = setTimeout(() => {
    console.log(diagonalOutput);
    clearTimeout(timer2);
}, 2000); // 13


إذا قمت بتشغيل هذا الرمز ، فستحصل على غير معرف في الحالتين الأوليين ، و 169 و 13 في الحالتين الثالثة والرابعة على التوالي. لماذا يحدث ذلك؟



هذا يرجع إلى حقيقة أننا نحاول الحصول على قيم المتغيرات المصدرة من middleware.js في main.js قبل تنفيذ الوظيفة غير المتزامنة. هل تتذكر أن لدينا وعدًا بانتظار الحل؟



لحل هذه المشكلة ، نحتاج إلى إبلاغ وحدة الاستيراد بطريقة ما بأن المتغيرات جاهزة للاستخدام.



الحلول


هناك طريقتان على الأقل لحل هذه المشكلة.



1. وعد التصدير للتهيئة


أولاً ، يمكن تصدير IIFE. تجعل الكلمة الأساسية غير المتزامنة الطريقة غير متزامنة ، وتعيد هذه الطريقة دائمًا وعدًا. لهذا السبب ، في المثال أدناه ، يقوم IIFE غير المتزامن بإرجاع وعد.



// middleware.js
import { square, diagonal } from "./library.js";

console.log("From Middleware");

let squareOutput;
let diagonalOutput;

const delay = (ms) => new Promise((resolve) => {
    const timer = setTimeout(() => {
        resolve(console.log("️"));
        clearTimeout(timer);
    }, ms);
});

//   ,   , 
export default (async () => {
    await delay(1000);
    squareOutput = square(13);
    diagonalOutput = diagonal(12, 5);
})();

export { squareOutput, diagonalOutput };


عند الوصول إلى المتغيرات المصدرة في main.js ، يمكنك الانتظار حتى يتم تنفيذ IIFE.



// main.js
import promise, { squareOutput, diagonalOutput } from "./middleware.js";

promise.then(() => {
    console.log(squareOutput); // 169
    console.log(diagonalOutput); // 169
    console.log("From Main");
});

const timer1 = setTimeout(() => {
    console.log(squareOutput);
    clearTimeout(timer1);
}, 2000); // 169

const timer2 = setTimeout(() => {
    console.log(diagonalOutput);
    clearTimeout(timer2);
}, 2000); // 13


على الرغم من أن هذا المقتطف يحل المشكلة ، فإنه يؤدي إلى مشاكل أخرى.



  • عند استخدام القالب المحدد ، عليك البحث عن الوعد المطلوب
  • إذا كانت وحدة نمطية أخرى تستخدم أيضًا المتغيرين "squareOutput" و "diagonalOutput" ، فيجب علينا التأكد من إعادة تصدير IIFE


هناك طريقة أخرى.



2. حل وعد IIFE مع المتغيرات المصدرة


في هذه الحالة ، بدلاً من تصدير المتغيرات بشكل فردي ، نعيدها من IIFE غير المتزامن. هذا يسمح للملف "main.js" ببساطة انتظار الوعد لحل واسترداد قيمته.



// middleware.js
import { square, diagonal } from "./library.js";

console.log("From Middleware");

let squareOutput;
let diagonalOutput;

const delay = (ms) => new Promise((resolve) => {
    const timer = setTimeout(() => {
        resolve(console.log("️"));
        clearTimeout(timer);
    }, ms);
});

//  
export default (async () => {
    await delay(1000);
    squareOutput = square(13);
    diagonalOutput = diagonal(12, 5);
    return { squareOutput, diagonalOutput };
})();

// main.js
import promise from "./middleware.js";

promise.then(({ squareOutput, diagonalOutput }) => {
    console.log(squareOutput); // 169
    console.log(diagonalOutput); // 169
    console.log("From Main");
});

const timer1 = setTimeout(() => {
    console.log(squareOutput);
    clearTimeout(timer1);
}, 2000); // 169

const timer2 = setTimeout(() => {
    console.log(diagonalOutput);
    clearTimeout(timer2);
}, 2000); // 13


ومع ذلك ، فإن هذا الحل له أيضًا بعض العيوب.



وفقًا للاقتراح ، "هذا النمط له عيب خطير لأنه يتطلب إعادة هيكلة كبيرة لمصدر المورد المرتبط في قوالب أكثر ديناميكية ووضع معظم جسم الوحدة في رد الاتصال. ثم () للسماح بالوحدات الديناميكية. وهذا يمثل انخفاضًا كبيرًا من حيث القدرة على التحليل الثابت ، وقابلية الاختبار ، وبيئة العمل ، وأكثر من ذلك مقارنة بوحدات ES2015 ".



كيف ينتظر "العالمية" حل هذه المشكلة؟



يسمح نظام انتظار المستوى الأعلى لنظام معياري بالعناية بالوفاء بالوعود وكيفية تفاعلها مع بعضها البعض.



// middleware.js
import { square, diagonal } from "./library.js";

console.log("From Middleware");

let squareOutput;
let diagonalOutput;

const delay = (ms) => new Promise((resolve) => {
    const timer = setTimeout(() => {
        resolve(console.log("️"));
        clearTimeout(timer);
    }, ms);
});

// "" await
await delay(1000);
squareOutput = square(13);
diagonalOutput = diagonal(12, 5);

export { squareOutput, diagonalOutput };

// main.js
import { squareOutput, diagonalOutput } from "./middleware.js";

console.log(squareOutput); // 169
console.log(diagonalOutput); // 13
console.log("From Main");

const timer1 = setTimeout(() => {
    console.log(squareOutput);
    clearTimeout(timer1);
}, 2000); // 169

const timer2 = setTimeout(() => {
    console.log(diagonalOutput);
    clearTimeout(timer2);
}, 2000); // 13


لا يتم تنفيذ أي من العبارات في main.js حتى يتم حل الوعود في middleware.js. هذا حل أنظف بكثير من الحلول.



المذكرة


الانتظار العالمي يعمل فقط مع وحدات ES. يجب تحديد التبعيات المستخدمة بشكل صريح. يوضح المثال أدناه من مستودع الاقتراح هذا جيدًا.



// x.mjs
console.log("X1");
await new Promise(r => setTimeout(r, 1000));
console.log("X2");
// y.mjs
console.log("Y");
// z.mjs
import "./x.mjs";
import "./y.mjs";
// X1
// Y
// X2


لن يخرج هذا المقتطف X1 و X2 و Y إلى وحدة التحكم ، كما قد تتوقع ، نظرًا لأن x و y هما وحدتان منفصلتان ، لا يرتبطان ببعضهما.



أوصي بشدة بدراسة قسم الأسئلة الشائعة في الاقتراح من أجل فهم أفضل للميزة المعنية.



التنفيذ



V8


يمكنك اختبار هذه الميزة الآن.



للقيام بذلك ، انتقل إلى الدليل حيث يوجد Chrome على جهازك. تأكد من إغلاق جميع علامات تبويب المتصفح. افتح Terminal وأدخل الأمر التالي:



chrome.exe --js-flags="--harmony-top-level-await"


يمكنك أيضًا تجربة هذه الميزة في Node.js. اقرأ هذا الدليل لمعرفة المزيد.



وحدات ES


تأكد من إضافة السمة "type" إلى علامة "script" بالقيمة "module".



<script type="module" src="./index.js"></script>


يرجى ملاحظة أنه بخلاف البرامج النصية العادية ، تتبع وحدات ES6 سياسة الأصل المشترك (مصدر فردي) (SOP) ومشاركة الموارد (CORS). لذلك ، من الأفضل العمل معهم على الخادم.



استخدم حالات



وفقًا للاقتراح ، فإن حالات استخدام الانتظار "العالمي" هي كما يلي:



مسار التبعية الديناميكي


const strings = await import(`/i18n/${navigator.language}`);


يسمح هذا للوحدات النمطية باستخدام قيم وقت التشغيل لحساب مسارات التبعية ويمكن أن تكون مفيدة لفصل رمز التطوير / الإنتاج ، والتدويل ، وتقسيم الشفرة بناءً على وقت التشغيل (المتصفح ، Node.js) ، إلخ



تهيئة الموارد


const connection = await dbConnector()


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



الخيار الاحتياطي


يوضح المثال أدناه كيف يمكن استخدام انتظار "عام" لتحميل تبعية بتطبيق احتياطي. إذا فشل الاستيراد من CDN A ، فسيتم إجراء الاستيراد من CDN B:



let jQuery;
try {
  jQuery = await import('https://cdn-a.example.com/jQuery');
} catch {
  jQuery = await import('https://cdn-b.example.com/jQuery');
}


نقد



قام ريتش هاريس بتجميع قائمة الانتقادات الموجهة إلى المستوى الأعلى من الانتظار. وهي تشمل ما يلي:



  • الانتظار "العالمي" يمكن أن يمنع تنفيذ التعليمات البرمجية
  • الانتظار "العالمي" يمكن أن يمنع الحصول على الموارد
  • عدم وجود دعم لوحدات CommonJS


ترد الإجابات على هذه التعليقات في اقتراح الأسئلة الشائعة:



  • نظرًا لأن العقد الفرعية (الوحدات) لديها القدرة على التنفيذ ، فلا يوجد في النهاية حظر للكود
  • يتم استخدام الانتظار "الشامل" أثناء مرحلة تنفيذ الرسم البياني للوحدة النمطية. في هذه المرحلة ، يتم استلام جميع الموارد وربطها ، لذلك لا يوجد خطر من منع الحصول على الموارد
  • انتظار المستوى الأعلى يقتصر على وحدات ES6. لم يتم التخطيط لدعم وحدات CommonJS ، مثل البرامج النصية العادية


مرة أخرى ، أوصي بشدة بقراءة الأسئلة الشائعة الخاصة بالاقتراح.



آمل أن أكون قادرًا على شرح جوهر الاقتراح المعني بطريقة يسهل الوصول إليها. هل ستنتهز هذه الفرصة؟ شارك برأيك في التعليقات.



All Articles