مقدمة
في هذه المقالة، سأعلّمك كيفية تحميل بعض JavaScript في المتصفّح وتنفيذه.
لا، انتظر، يُرجى الرجوع. أعلم أنّ هذا يبدو عاديًا وبسيطًا، ولكن تذكَّر أنّ هذا يحدث في المتصفّح حيث يصبح الإجراء البسيط نظريًا مشكلة غريبة بسبب الإصدارات القديمة. تتيح لك معرفة هذه الأخطاء اختيار الطريقة الأسرع والأقل تعطيلًا لتحميل النصوص البرمجية. إذا كان لديك جدول زمني ضيق، يمكنك الانتقال إلى المرجع السريع.
بالنسبة إلى المبتدئين، إليك كيفية تحديد المواصفات الطرق المختلفة التي يمكن بها تنزيل نص برمجي وتنفيذه:

مثل جميع مواصفات WHATWG، يبدو في البداية أنّه نتيجة انفجار قنبلة عنقودية في مصنع للكلمات المتقاطعة، ولكن بعد قراءته للمرة الخامسة ومسح الدم من عينيك، يصبح الأمر مثيرًا للاهتمام حقًا:
يتضمّن النص الأول ما يلي:
<script src="//other-domain.com/1.js"></script>
<script src="2.js"></script>
يا لها من بساطة رائعة. في هذه الحالة، سينزِّل المتصفّح كلا النصّين البرمجيَّين بشكل موازٍ وينفِّذهما في أقرب وقت ممكن مع الحفاظ على ترتيبهما. لن يتم تنفيذ “2.js” إلا بعد تنفيذ “1.js” (أو تعذّر تنفيذه)، ولن يتم تنفيذ “1.js” إلا بعد تنفيذ النص البرمجي أو جدول الأنماط السابق، وما إلى ذلك.
ويمنع المتصفّح عرض المزيد من الصفحة أثناء حدوث كل ذلك. ويعود ذلك إلى واجهات برمجة تطبيقات DOM من "العصر الأول للويب" التي تسمح بإلحاق سلاسل إلى المحتوى الذي يعالجه المُحلِّل، مثل document.write
. سيواصل المتصفّحات الأحدث فحص المستند أو تحليله في الخلفية وبدء عمليات تنزيل للمحتوى الخارجي الذي قد يحتاجه (مثل JavaScript والصور وCSS وما إلى ذلك)، ولكن سيظلّ عرض المحتوى محظورًا.
لهذا السبب، ننصح بوضع عناصر النصوص البرمجية في نهاية المستند، لأنّ ذلك يمنع ظهور قدر قليل من المحتوى قدر الإمكان. ويعني ذلك أنّ المتصفح لا يرى النص البرمجي إلى أن ينزّل كل محتوى HTML، وعند هذه المرحلة يبدأ في تنزيل محتوى آخر، مثل CSS والصور وإطارات iframe. إنّ المتصفّحات الحديثة ذكية بما يكفي لمنح الأولوية لبرنامج JavaScript على الصور، ولكن يمكننا تحسين ذلك.
شكرًا، إيهاب (لا، لست أقصد السخرية)
<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>
لاحظت Microsoft هذه المشاكل في الأداء وأدخَلت ميزة "التأجيل" في Internet Explorer 4. يعني ذلك ببساطة "أتعهد بعدم إدخال عناصر في المدقّق باستخدام عناصر مثل document.write
. إذا خالفت هذا الوعد، يمكنك معاقبة أيًّا منّا بالطريقة التي تراها مناسبة". تم تضمين هذه السمة في HTML4 وظهرت في متصفّحات أخرى.
في المثال أعلاه، سينزِّل المتصفّح كلا البرنامجَين النصيَّين بالتوازي وينفِّذهما قبل بدء تنفيذ DOMContentLoaded
مباشرةً، مع الحفاظ على ترتيبهما.
وأصبح "تأجيل" عبارة عن فوضى متشابكة، مثل قنبلة عنقودية في مصنع للأغنام. بين سمتَي "src" و"defer"، وعلامات النصوص البرمجية في مقابل النصوص البرمجية المُضافة ديناميكيًا، لدينا 6 أنماط لإضافة نص برمجي. بطبيعة الحال، لم تتفق المتصفّحات على الطلب الذي يجب تنفيذه. كتبت Mozilla مقالة رائعة عن المشكلة في عام 2009.
وضّح فريق WHATWG السلوك، معلناً أنّ "التأجيل" ليس له أي تأثير على النصوص البرمجية التي تمت إضافتها ديناميكيًا أو التي لا تحتوي على سمة "src". بخلاف ذلك، من المفترض أن يتم تشغيل النصوص البرمجية المؤجّلة بعد تحليل المستند، بترتيب إضافتها.
شكرًا، إيهاب (حسنًا، أقصد ذلك بشكل ساخر)
يعطي ويأخذ. للأسف، هناك خلل خطير في IE4-9 يمكن أن يؤدي إلى تنفيذ النصوص البرمجية بترتيب غير متوقّع. إليك ما يحدث:
1.js
console.log('1');
document.getElementsByTagName('p')[0].innerHTML = 'Changing some content';
console.log('2');
2.js
console.log('3');
بافتراض توفّر فقرة في الصفحة، يكون الترتيب المتوقّع للسجلّات هو [1، 2، 3]، على الرغم من أنّه في IE9 والإصدارات الأقدم، يظهر الترتيب على النحو التالي: [1، 3، 2]. تؤدي عمليات معيّنة في DOM إلى إيقاف تنفيذ النص البرمجي الحالي مؤقتًا وتنفيذ النصوص البرمجية الأخرى المعلّقة قبل المتابعة.
ومع ذلك، حتى في عمليات التنفيذ التي لا تتضمّن أخطاء، مثل IE10 والمتصفّحات الأخرى، يتم تأخير تنفيذ النصوص البرمجية إلى أن يتم تنزيل المستند بالكامل وتحليله. قد يكون هذا مناسبًا إذا كنت ستنتظر DOMContentLoaded
على أي حال، ولكن إذا كنت تريد تحسين الأداء بشكل كبير، يمكنك بدء إضافة مستمعين وبدء التشغيل مبكرًا…
استخدام HTML5 لتقديم المساعدة
<script src="//other-domain.com/1.js" async></script>
<script src="2.js" async></script>
وفّرت لنا لغة HTML5 سمة جديدة، وهي "async"، والتي تفترض أنّك لن تستخدم document.write
، ولكنّها لا تنتظر حتى يتم تحليل المستند لتنفيذه. سينزِّل المتصفّح كلا النصَّين البرمجيَّين بشكل موازٍ وينفِّذهما في أقرب وقت ممكن.
بسبب أنّه سيتم تنفيذهما في أقرب وقت ممكن، قد يتم تنفيذ “2.js” قبل “1.js”. لا بأس بذلك إذا كانا مستقلّين، فربما يكون “1.js” نصًا برمجيًا للتتبّع لا علاقة له بـ “2.js”. ولكن إذا كان “1.js” نسخة من jQuery على شبكة توصيل المحتوى (CDN) تعتمد عليها “2.js”، ستظهر أخطاء على صفحتك، مثل قنبلة عنقودية في… لا أعرف… ليس لدي أيّ تفسير لذلك.
أعرف ما نحتاجه، مكتبة JavaScript.
إنّ الهدف هو تنزيل مجموعة من النصوص البرمجية على الفور بدون حظر العرض وتنفيذها في أقرب وقت ممكن بالترتيب الذي تمت إضافتها به. للأسف، لا يسمح لك تنسيق HTML بإجراء ذلك.
تم حلّ المشكلة باستخدام JavaScript بأشكال مختلفة. وقد تطلّب منك البعض إجراء تغييرات على JavaScript، من خلال لفّها في دالة استدعاء تستدعيها المكتبة بالترتيب الصحيح (مثل RequireJS). وكان الآخرون يستخدمون XHR للتنزيل بشكل موازٍ ثم eval()
بالترتيب الصحيح، ما لم يكن لديهم عنوان CORS وكان المتصفّح متوافقًا معه. واستخدم البعض حتى أساليب خداعية فائقة، مثل LabJS.
كانت عمليات الاختراق تتضمن خداع المتصفّح لتنزيل المورد بطريقة تؤدي إلى بدء حدث عند اكتمال التنزيل، ولكن مع تجنُّب تنفيذه. في LabJS، ستتم إضافة النص البرمجي بنوع MIME غير صحيح، مثل <script type="script/cache" src="...">
. بعد تنزيل جميع النصوص البرمجية، ستتم إضافتها مرة أخرى بنوع صحيح، على أمل أن يحصل المتصفّح عليها مباشرةً من ذاكرة التخزين المؤقت وينفّذها على الفور بالترتيب. وكان هذا الإجراء يعتمد على سلوك ملائم ولكن غير محدّد، وتعطّل عندما أعلنت HTML5 أنّ المتصفحات يجب ألا تنزّل نصوص برمجية بنوع غير معروف. تجدر الإشارة إلى أنّ LabJS تكيّف مع هذه التغييرات ويستخدم الآن مجموعة من الطرق الواردة في هذه المقالة.
ومع ذلك، تواجه أدوات تحميل النصوص البرمجية مشكلة في الأداء، وعليك الانتظار إلى أن يتم تنزيل JavaScript الخاص بالمكتبة وتحليله قبل أن يبدأ تنزيل أي من النصوص البرمجية التي تُديرها. كيف سنحمّل أداة تحميل النصوص البرمجية؟ كيف سنحمّل النص البرمجي الذي يُخبر أداة تحميل النصوص البرمجية بما يجب تحميله؟ من يشاهد محتوى Watchmen؟ لماذا أظهر عارياً؟ هذه كلها أسئلة صعبة.
بوجهٍ عام، إذا كان عليك تنزيل ملف نصي إضافي قبل التفكير في تنزيل نصوص أخرى، يعني ذلك أنّك خسرت معركة الأداء على الفور.
نموذج DOM لإنقاذ الموقف
يمكن العثور على الإجابة في مواصفات HTML5، إلا أنّها مخفية في أسفل قسم تحميل النصوص البرمجية.
لنترجم ذلك إلى "كائن من الأرض":
[
'//other-domain.com/1.js',
'2.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
document.head.appendChild(script);
});
النصوص البرمجية التي يتم إنشاؤها ديناميكيًا وإضافتها إلى المستند تكون غير متزامنة تلقائيًا، ولا تحظر العرض والتنفيذ فور تنزيلها، ما يعني أنّها قد تظهر بترتيب غير صحيح. ومع ذلك، يمكننا وضع علامة صراحةً على أنّها غير متزامنة:
[
'//other-domain.com/1.js',
'2.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
ويمنح ذلك النصوص البرمجية مجموعة من السلوكيات التي لا يمكن تحقيقها باستخدام HTML العادي. وبما أنّها بشكل صريح غير متزامنة، تتم إضافة النصوص البرمجية إلى قائمة انتظار التنفيذ، وهي القائمة نفسها التي تتم إضافتها إليها في مثال HTML العادي الأول. ومع ذلك، يتم تنفيذها خارج عملية تحليل المستند، وذلك لأنّها يتم إنشاؤها ديناميكيًا، لذا لا يتم حظر العرض أثناء تنزيلها (لا تخلط بين تحميل النصوص البرمجية غير المتزامنة وطلبات XHR المتزامنة، وهي ليست جيدة أبدًا).
يجب تضمين النص البرمجي أعلاه في العنوان في الصفحات، وإضافة عمليات تنزيل النصوص البرمجية إلى "قائمة الانتظار" في أقرب وقت ممكن بدون إيقاف العرض التدريجي، وتنفيذها في أقرب وقت ممكن بالترتيب الذي حدّدته. يمكن تنزيل ملف “2.js” قبل ملف “1.js”، ولكن لن يتم تنفيذه إلا بعد تنزيل ملف “1.js” وتنفيذه بنجاح أو تعذُّر تنفيذ أي منهما. مرحبًا، تم تنزيل الملفات بشكل غير متزامن ولكن تم تنفيذها بالترتيب.
يتوافق تحميل النصوص البرمجية بهذه الطريقة مع كل ما يتوافق مع السمة async، باستثناء Safari 5.0 (يمكن استخدام الإصدار 5.1). بالإضافة إلى ذلك، جميع إصدارات Firefox وOpera متوافقة، لأنّ الإصدارات التي لا تتيح استخدام السمة async تنفِّذ النصوص البرمجية المُضافة ديناميكيًا بالترتيب الذي تمت إضافتها به إلى المستند على أي حال.
هل هذه هي الطريقة الأسرع لتحميل النصوص البرمجية؟ أليس كذلك؟
إذا كنت تحدّد ديناميكيًا النصوص البرمجية التي تريد تحميلها، نعم، وإلا، قد لا يكون الأمر كذلك. في المثال أعلاه، على المتصفّح تحليل النص البرمجي وتنفيذه لمعرفة النصوص البرمجية التي يجب تنزيلها. يؤدي ذلك إلى إخفاء نصوصك البرمجية عن برامج فحص التحميل المُسبَق. وتستخدم المتصفّحات هذه الماسحات الضوئية لاكتشاف المراجع على الصفحات التي يُرجّح أن تزورها بعد ذلك، أو لاكتشاف مراجع الصفحة عندما يكون المُحلِّل محظورًا من قِبل مرجع آخر.
يمكننا إعادة إضافة ميزة الاكتشاف من خلال وضع ما يلي في رأس المستند:
<link rel="subresource" href="//other-domain.com/1.js">
<link rel="subresource" href="2.js">
يُعلم هذا المتصفّح بأنّ الصفحة تحتاج إلى 1.js و2.js. تشبه الدالة link[rel=subresource]
الدالة link[rel=prefetch]
، ولكنّها ذات دلالات مختلفة. لا يتوفّر هذا الخيار حاليًا إلا في Chrome، وعليك تحديد النصوص البرمجية التي تريد تحميلها مرّتين، مرّة واحدة من خلال عناصر الرابط ومرّة أخرى في النص البرمجي.
تصحيح: لقد ذكرت في الأصل أنّ هذه الملفات يتم رصدها بواسطة الماسح الضوئي لميزة "التحميل المُسبَق"، ولكن هذا غير صحيح، بل يتم رصدها بواسطة المدقّق العادي. ومع ذلك، يمكن أن يرصدها "أداة فحص التحميل المُسبَق"، ولكنّها لا ترصدها بعد، في حين أنّه لا يمكن أبدًا تحميل النصوص البرمجية المضمّنة في رمز قابل للتنفيذ مُسبَقًا. شكرًا يواف ويس الذي صحّح معلوماتي في التعليقات.
أجد هذه المقالة محبطة
إنّ هذا الموقف مزعج ومن الطبيعي أن تشعر بالضيق. لا تتوفّر طريقة غير متكرّرة وواضحة لتنزيل النصوص البرمجية بسرعة وبشكل غير متزامن مع التحكّم في ترتيب التنفيذ. باستخدام HTTP2/SPDY، يمكنك تقليل الوقت المستغرَق في معالجة الطلبات إلى الحدّ الذي يجعل إرسال النصوص البرمجية في ملفات صغيرة متعددة قابلة للتخزين المؤقت بشكلٍ فردي أسرع طريقة. تخيل ما يلي:
<script src="dependencies.js"></script>
<script src="enhancement-1.js"></script>
<script src="enhancement-2.js"></script>
<script src="enhancement-3.js"></script>
…
<script src="enhancement-10.js"></script>
يتعامل كل نص برمجي لتحسين مع مكوّن صفحة معيّن، ولكنه يتطلّب دوالّ مساعدة في dependencies.js. من الناحية المثالية، نريد تنزيل كل الملفات بشكل غير متزامن، ثم تنفيذ النصوص البرمجية للتحسين في أقرب وقت ممكن، بأي ترتيب، ولكن بعد dependencies.js. إنه تحسين تدريجي تدريجي. لا تتوفّر طريقة تعريفية لتحقيق ذلك ما لم يتم تعديل النصوص البرمجية نفسها لتتبُّع حالة تحميل dependencies.js. ولا يؤدي استخدام async=false إلى حلّ هذه المشكلة، لأنّ تنفيذ enhancement-10.js سيتم حظره من 1 إلى 9. في الواقع، هناك متصفّح واحد فقط يتيح إجراء ذلك بدون عمليات اختراق…
لدى IE فكرة.
يُحمِّل Internet Explorer النصوص البرمجية بشكل مختلف عن المتصفّحات الأخرى.
var script = document.createElement('script');
script.src = 'whatever.js';
يبدأ Internet Explorer في تنزيل whatever.js الآن، ولا تبدأ المتصفّحات الأخرى في التنزيل إلى أن تتم إضافة النص البرمجي إلى المستند. يحتوي متصفّح IE أيضًا على حدث "readystatechange" وسمة "readystate" اللتين تُعلمنا بمستوى تقدّم التحميل. وهذا مفيد جدًا، لأنّه يتيح لنا التحكّم في تحميل النصوص البرمجية وتنفيذها بشكل مستقل.
var script = document.createElement('script');
script.onreadystatechange = function() {
if (script.readyState == 'loaded') {
// Our script has download, but hasn't executed.
// It won't execute until we do:
document.body.appendChild(script);
}
};
script.src = 'whatever.js';
يمكننا إنشاء نماذج معقدة للتبعية من خلال اختيار وقت إضافة النصوص البرمجية إلى المستند. يتيح متصفّح IE هذا النموذج منذ الإصدار 6. هذا أمر مثير للاهتمام، ولكنّه لا يزال يواجه مشكلة اكتشاف أداة التحميل المُسبَق نفسها التي تواجهها async=false
.
يكفي. كيف يمكنني تحميل النصوص البرمجية؟
حسنًا. إذا كنت تريد تحميل النصوص البرمجية بطريقة لا تحظر العرض ولا تتضمّن تكرارًا وتتوافق بشكلٍ رائع مع المتصفّحات، إليك ما أقترحه:
<script src="//other-domain.com/1.js"></script>
<script src="2.js"></script>
هذا كلّ ما في الأمر. في نهاية عنصر النص نعم، إنّ العمل كمطوّر ويب يشبه إلى حد كبير عمل الملك سيزيفوس (بوم! 100 نقطة لإشارة إلى الأساطير اليونانية. إنّ القيود المفروضة على HTML والمتصفّحات تمنعنا من تحقيق نتائج أفضل بكثير.
آمل أن تساعدنا وحدات JavaScript من خلال توفير طريقة توضيحية غير حظر لتحميل النصوص البرمجية ومنح إمكانية التحكّم في ترتيب التنفيذ، على الرغم من أنّ ذلك يتطلّب كتابة النصوص البرمجية كوحدات.
هل هناك طريقة أفضل يمكننا استخدامها الآن؟
حسنًا، للحصول على نقاط إضافية، إذا كنت تريد تحسين الأداء بشكل كبير ولا تمانع بعض التعقيد والتكرار، يمكنك الجمع بين بعض الحيل المذكورة أعلاه.
أولاً، نضيف بيان الموارد الفرعية لمعدّلات التحميل المُسبَق:
<link rel="subresource" href="//other-domain.com/1.js">
<link rel="subresource" href="2.js">
بعد ذلك، في القسم العلوي من المستند، نحمِّل النصوص البرمجية باستخدام JavaScript، باستخدام async=false
، مع الرجوع إلى طريقة تحميل النصوص البرمجية المستندة إلى حالة الاستعداد في Internet Explorer، مع الرجوع إلى طريقة التأخير.
var scripts = [
'1.js',
'2.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];
// Watch scripts load in IE
function stateChange() {
// Execute as many scripts in order as we can
var pendingScript;
while (pendingScripts[0] && pendingScripts[0].readyState == 'loaded') {
pendingScript = pendingScripts.shift();
// avoid future loading events from this script (eg, if src changes)
pendingScript.onreadystatechange = null;
// can't just appendChild, old IE bug if element isn't closed
firstScript.parentNode.insertBefore(pendingScript, firstScript);
}
}
// loop through our script urls
while (src = scripts.shift()) {
if ('async' in firstScript) { // modern browsers
script = document.createElement('script');
script.async = false;
script.src = src;
document.head.appendChild(script);
}
else if (firstScript.readyState) { // IE<10
// create a script and add it to our todo pile
script = document.createElement('script');
pendingScripts.push(script);
// listen for state changes
script.onreadystatechange = stateChange;
// must set src AFTER adding onreadystatechange listener
// else we'll miss the loaded event for cached scripts
script.src = src;
}
else { // fall back to defer
document.write('<script src="' + src + '" defer></'+'script>');
}
}
بعد تطبيق بعض الحيل والتصغير، أصبح حجم النص البرمجي 362 بايت بالإضافة إلى عناوين URL للنص البرمجي:
!function(e,t,r){function n(){for(;d[0]&&"loaded"==d[0][f];)c=d.shift(),c[o]=!i.parentNode.insertBefore(c,i)}for(var s,a,c,d=[],i=e.scripts[0],o="onreadystatechange",f="readyState";s=r.shift();)a=e.createElement(t),"async"in i?(a.async=!1,e.head.appendChild(a)):i[f]?(d.push(a),a[o]=n):e.write("<"+t+' src="'+s+'" defer></'+t+">"),a.src=s}(document,"script",[
"//other-domain.com/1.js",
"2.js"
])
هل يستحق الأمر استخدام وحدات البايت الإضافية مقارنةً بتضمين نص برمجي بسيط؟ إذا كنت تستخدِم JavaScript لتحميل النصوص البرمجية بشكل مشروط، مثلما تفعل BBC، يمكنك أيضًا الاستفادة من بدء عمليات التنزيل هذه في وقت أبكر. بخلاف ذلك، قد لا يكون الأمر كذلك، لذا يمكنك الاستمرار في استخدام طريقة نهاية النص البسيطة.
أخيرًا، عرفت الآن سبب اتساع قسم تحميل النصوص البرمجية في WHATWG. أحتاج إلى مشروب.
مرجع سريع
عناصر النصوص البرمجية البسيطة
<script src="//other-domain.com/1.js"></script>
<script src="2.js"></script>
ما ورد في المواصفات: يتم تنزيلها معًا وتنفيذها بالترتيب بعد أي ملفات CSS في انتظار المراجعة، ومنع العرض إلى أن تكتمل. المتصفّحات: نعم يا سيدي
تأجيل
<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>
ما ورد في المواصفات: يتم تنزيلها معًا، وتنفيذها بالترتيب قبل DOMContentLoaded مباشرةً. تجاهل "defer" في النصوص البرمجية التي لا تحتوي على "src". يُظهر IE 10 والإصدارات الأقدم ما يلي: قد نفِّذ 2.js في منتصف تنفيذ 1.js. أليس هذا ممتعًا؟ يشير المتصفّحات باللون الأحمر إلى ما يلي: ليس لديّ أي فكرة عن هذا الإجراء "تأخير"، سأحمّل النصوص البرمجية كما لو لم تكن موجودة. المتصفّحات الأخرى: حسنًا، ولكن قد لا أتجاهل "defer" في النصوص البرمجية التي لا تحتوي على "src".
غير متزامنة
<script src="//other-domain.com/1.js" async></script>
<script src="2.js" async></script>
ما ورد في المواصفات: يتم تنزيلها معًا وتنفيذها بالترتيب الذي يتم تنزيلها به. يعرض المتصفّحات باللون الأحمر ما يلي: ما المقصود بـ "غير متزامنة"؟ سأحمّل النصوص البرمجية كما لو لم تكن موجودة. ما يقوله المتصفّح الآخر: حسنًا.
غير متزامن خطأ
[
'1.js',
'2.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
البيانات المحدّدة: يتم تنزيلها معًا، ويتم تنفيذها بالترتيب بعد تنزيلها بالكامل. Firefox < 3.6، Opera: ليس لديّ أي فكرة عن هذا العنصر "غير المتزامن"، ولكن يحدث أنّني أُنفّذ النصوص البرمجية المُضافة من خلال JS بالترتيب الذي تمت إضافتها به. يُظهر Safari 5.0 ما يلي: أتفهّم "async"، ولكن لا أتفهّم ضبطه على "false" باستخدام JavaScript. سأنفّذ نصوصك البرمجية فور وصولها، بغض النظر عن ترتيبها. يعرض متصفّح Internet Explorer الذي يقلّ إصداره عن 10 ما يلي: لا نعرف أيّ معلومات عن "async"، ولكن هناك حلّ بديل باستخدام "onreadystatechange". يعرض المتصفّحات الأخرى باللون الأحمر ما يلي: لا نفهم هذا العنصر "async"، وسننفّذ نصوصك البرمجية فور وصولها، بأيّ ترتيب. تشير كل العناصر الأخرى إلى: أنا صديقك، وسننفّذ ذلك وفقًا للقواعد.