لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 05/06/23 في كل الموقع
-
3 نقاط
-
2 نقاط
-
لا يوجد طريقة أفضل من كتابة الكود وبناء المشاريع عند تعلم البرمجة، فمهما شاهدت من شروحات وقرأت دروس ومقالات لن تحقق الكثير سوى أنك تمتلك معلومات فقط، ولكنها ليس منظمة وليست لها فائدة إلا من خلال التطبيق العملي لزيادة مهاراتك وتثبيت المعلومات. وتستطيع رؤية تصاميم كاملة من خلال المواقع التي تم ذكرها في النقاشات التالية: وإذا كان الأمر صعب عليك بخصوص تنفيذ تصميم كامل، فتستطيع تصميم مكون واحد فقط كقسم من الصفحة ثم تنفيذ مكون آخر حتى تنتهي من الصفحة، ثم تحاول ربط المكونات ببعضها. أو استمر في إنشاء المكونات أو التصاميم الصغيرة ثم بعد فترة قم بإنشاء تصميم كامل مكون من أكثر من صفحة.2 نقاط
-
1 نقطة
-
احتاج قوالب لمواقع جاهزه مجانيه حتى استطيع التدرب عليها وتنفيذها عن طريق react، فاين اجدها وهل هذه طريقة جيده للممارسه وتقويه مهاراتى فى البرمجه او يوجد طريقه افضل ؟1 نقطة
-
ما تريده تستطيع تنفيذه باستخدام HTML و جافاسكريبت بالشكل التالي: <!DOCTYPE html> <html> <head> <title>شراء المنتج</title> </head> <body> <div> <img src="صورة_المنتج.jpg" alt="صورة المنتج"> <p>وصف المنتج</p> <button onclick="redirectToCheckout()">شراء المنتج</button> </div> <div id="checkoutForm" style="display: none;"> <h2>تأكيد الشراء</h2> <form onsubmit="sendWhatsAppMessage(event)"> <label for="name">الاسم:</label> <input type="text" id="name" required><br><br> <label for="address">العنوان:</label> <input type="text" id="address" required><br><br> <label for="phone">رقم التليفون:</label> <input type="text" id="phone" required><br><br> <input type="submit" value="تأكيد الشراء"> </form> </div> <script> function redirectToCheckout() { document.getElementById('checkoutForm').style.display = 'block'; } function sendWhatsAppMessage(event) { event.preventDefault(); const name = document.getElementById('name').value; const address = document.getElementById('address').value; const phone = document.getElementById('phone').value; const productLink = window.location.href; const message = "اسم المنتج: " + productLink + "%0A" + "الاسم: " + name + "%0A" + "العنوان: " + address + "%0A" + "رقم التليفون: " + phone; const whatsappURL = "https://wa.me/رقم_الواتساب?text=" + encodeURIComponent(message); window.open(whatsappURL); } </script> </body> </html> وعليك باستبدال "صورة_المنتج.jpg" بمسار صحيح لصورة المنتج الخاصة بك، واستبدال "رقم_الواتساب" برقم واتساب حقيقي لحسابك. ووظيفية الكود هي إنشاء قسم يحتوي على صورة المنتج ووصفه، ثم زر "شراء المنتج"، وعند النقر على الزر، يتم عرض نموذج التحقق من الشراء، وبمجرد ملء النموذج والنقر على زر "تأكيد الشراء"، يتم فتح نافذة واتساب جديدة تحتوي على رابط المنتج والبيانات التي تم إدخالها في نموذج التحقق. حيث ستظهر البيانات في صيغة نصية، تحتوي على اسم المنتج، الاسم، العنوان، ورقم التليفون، وسيتم إرسالها إلى الرقم الذي قمت بتعيينه لحساب الواتساب الخاص بك.1 نقطة
-
السلام عليكم. أريد رفع رفع مشروع ريآكت على منصة netlify عن طريق المستودع github مثلما هو مبين بالرابط التالي. المشكل هو أن الموقع لم يرفع منذ أكثر من 45 دقيقة. قمت بإعادة الرفع أكثر من مرة. هذا رابط المشروع على github. شكرا على التوضيح.1 نقطة
-
تحتاج إلى استخدام مكتبة خارجية أو برنامج مساعد يمكنك من تشغيل الملفات الصوتية، حيث أن لغة التجميع تعتبر لغة منخفضة المستوى وتركز بشكل أساسي على التحكم المباشر في وحدات المعالجة المركزية ومكونات النظام، وليست مصممة بشكل مباشر للتعامل مع الملفات الصوتية. فإذا كنت مهتمًا بتشغيل ملف موسيقى في برنامج بلغة التجميع، عليك بالبحث عن مكتبات خارجية تقوم بتنفيذ هذه المهمة وتوفير وظائف للتلاعب بالملفات الصوتية، وتعتمد الطريقة الدقيقة على نظام التشغيل الذي تستخدمه ونوع المعالج. والخطوات العامة هي البحث عن مكتبات تدعم تنسيقات الملفات الصوتية المختلفة مثل MP3 أو WAV. أيضًا تستطيع استخدام واجهات برمجة التطبيقات (APIs) المتاحة في بعض أنظمة التشغيل لتشغيل الملفات الصوتية من خلال برنامج بلغة التجميع، وهي الطريقة الأسهل، حيث يمكنك استدعاء واجهة برمجة التطبيقات المناسبة من برنامج التجميع الخاص بك. ومع ذلك، يجب أن تلاحظ أن استخدام لغة التجميع لتشغيل ملفات موسيقى أمرًا معقدًا ويتطلب خبرة متقدمة في لغة التجميع وهياكل البيانات وتنسيقات الملفات الصوتية.، ومن الأفضل استخدام لغة برمجة أعلى مستوى مثل C أو C++ أو Python لتنفيذ هذه المهمة، حيث تتوفر بهذه اللغات مكتبات جاهزة.1 نقطة
-
إن الموضوع معقد جداً و من المستحيل شرحه في تعليق، و لكن يمكنني توجيهك لكيفية تعلم ما يلزم. في البداية عليك تحديد لماذا تريد القيام بذلك؟ هل هو مجرد تحدٍ؟ حيث أنني لا أنصحك بإضاعة وقتك في ذلك ما لم يكن لديك هدف حقيقي لذلك. في حال كان هدفك مجرد بناء مشغل موسيقى فمن الجيد استعمال لغات عالية المستوى كال c++ أو python مثلاً فهذه اللغات تحوي الكثير من أطر العمل التي تساعدك على ذلك. أما في حال كان لديك هدف ما، ففي البداية يجب عليك تعلم أساسيات ال assembly، و محاولة بناء تطبيق بسيط جداً لتجربة مهاراتك، بعدها يمكنك الانتقال إلى المرحلة التالية في بناء التطبيق. من المفيد الإطلاع على ما قام به آخرون لحل هذه المشكلة، هذا قد يساعدك على كتابة الكود، مثلاً يمكنك الإطلاع على هذا المشروع على github من هنا.1 نقطة
-
عند اضافة ;flex-wrap:wrap; و display:flex و عند تصغير الصفحة سينزل عنصر تحت بعض بس لدي لن ينزل العنصر تحت بعض عند تصغير الصفحة مل حل المشكلة عند h. h div. و شكرا للمساعدة الصفحة 1.css الصفحة 1.html1 نقطة
-
المشروع الخاص بك هو مشروع React ولذلك أنت بحاجة إلى قراءة الإرشادات التالية: وهل تواجه رسالة خطأ أو ما هي حالة المشروع على Netlify؟1 نقطة
-
1 نقطة
-
انا عمري 13 او 12 لدي شغف كبير في تعلم البرمجة ولقد تعلمت قليل من لغة بايثون ولدي الشغف لتعلم الكثير ولقد وجدت اكاديمية حسوب ولقد تحمست كثيرا, ولكن هل يمكنني الاستفادة من الدورة كمتابعة او لا يمكنني لصغر عمري؟ وهل الشهادة يمكنني الاستفادة منها ؟ وهل استفيد من السيرة الذاتية التي تقدمونها؟ وشكرا.1 نقطة
-
السؤال عن الفرق بين main و master هو سؤال عام جدا فقد يكون لكلمة main دلالة معينة مثلا في لغة جافا او c++، وفي سياق git في مثالك هما مثلما أشير اليه سابقا.1 نقطة
-
1 نقطة
-
في Github ، يشير "main" إلى الفرع الرئيسي لمستودع Git. بينما "master" كان الاسم الافتراضي لفرع Git الرئيسي في الماضي ، ولكنه تم تغييره في Github إلى "main" في عام 2020. هناك عدة أسباب لتغيير Github للاسم الافتراضي لفرع Git الرئيسي من "master" إلى "main": الاسم "master" قد يحمل طابعًا عنصريًا غير مرغوب فيه لبعض الأشخاص. الاسم "main" يمكن أن يكون أكثر دقة في وصف الفرع الرئيسي ، حيث يعكس فكرة أن الفرع الرئيسي هو المسار الرئيسي لتطوير البرنامج أو المشروع. قد يتم استخدام "master" كاسم لفرع مخصص للإصدارات المستقرة في بعض المشاريع ، مما يؤدي إلى الخلط بين الفرع الرئيسي وفرع الإصدارات المستقرة. يمكن للمستخدمين تغيير اسم الفرع الرئيسي إلى أي اسم آخر يريدونه ، ولكن "main" هو الاسم المقترح الذي يستخدمه Github حاليًا. يمكن الوصول إلى إعدادات الفرع الرئيسي في Github من خلال النقر على "Settings" ثم "Branches" في قائمة الإعدادات.1 نقطة
-
الفرق بين main و master يختلف حسب السياق الذي يستخدم فيه كل منهما، ولكن بشكل عام: في Git: كان يتم استخدام الفرع الافتراضي "master" للإشارة إلى النسخة الرئيسية من المستودع، ولكن في الآونة الأخيرة، تم تغيير اسم الفرع الافتراضي إلى "main" في الإصدارات الحديثة من Git، تماشيًا مع الجهود العالمية للحد من استخدام المصطلحات التي قد تسيء إلى بعض الأشخاص مثلما اشار المدرب مصطفى. أما في البرمجة بشكل عام: فعند كتابة برنامج، يعتبر الدالة main هي دالة البداية التي يتم تشغيلها في البداية، وتعتبر بمثابة الباب الرئيسي للبرنامج، ويتم العودة إليها في النهاية. يجب الانتباه إلى أن استخدام "main" أو "master" يختلف تبعاً للسياق ويمكن أن يختلف في الاستخدام بين اللغات البرمجية وأدوات إدارة المشاريع المختلفة. وفي حالتك، هو ليس أكثر من تسمية الفرع الرئيسي لمستودع git لديك.1 نقطة
-
في GitHub، تُستخدم تعابير "master" و "main" للإشارة إلى الفرع الافتراضي الرئيسي لمستودع Git. والفرق بينهما هو فقط في الاسم وليس في الوظيفة أو الأداء. فقديمًا، كان اسم الفرع الافتراضي في GitHub هو "master"، وهذا الاسم استُخدم للإشارة إلى الفرع الرئيسي الذي يحتوي على النسخة الرئيسية من المشروع. ومع ذلك، بدأت GitHub في اعتماد مصطلح "main" بدلاً من "master" بسبب القلق المتزايد حول المصطلح "master" وتأثيره اللاحق على العنصرية والتمييز العنصري. لذا، لجعل GitHub أكثر شمولًا ومتعدد الثقافات، قامت GitHub بتغيير اسم الفرع الافتراضي من "master" إلى "main". ومع ذلك، لا يزال بإمكان المستخدمين استخدام اسم "master" إذا كانوا يفضلون ذلك. أي أن استخدام "master" أو "main" في GitHub يعتبر مسألة تفضيل شخصي، ولا يؤثر ذلك على أداء مستودع Git أو إمكانياته. ولتحويل اسم الفرع الافتراضي في مستودع Git من "master" إلى "main" أو العكس، يمكنك اتباع الخطوات التالية: قم بفتح مستودع Git الذي ترغب في تغيير اسم الفرع الافتراضي فيه على GitHub. انتقل إلى صفحة الإعدادات (Settings) للمستودع. ابحث عن قسم يسمى "Branches" أو "Default Branch"، وسيعرض الفرع الافتراضي الحالي (مثل "master" أو "main"). انقر على الزر أو الرابط المرتبط بتغيير الفرع الافتراضي. ستظهر لك قائمة تحتوي على الفروع المتاحة في المستودع، حدد الفرع الجديد الذي ترغب في جعله الافتراضي (مثل "master" أو "main"). قد يُطلب منك تأكيد التغيير وإدخال كلمة مرورك. بعد التأكيد، ستتم عملية تحويل الفرع الافتراضي، وسيتم استخدام الفرع الجديد في المستقبل. يرجى ملاحظة أن تحويل اسم الفرع الافتراضي على GitHub لا يؤثر على تاريخ أو تاريخ الالتزامات الموجودة في المستودع، وهو مجرد تغيير في الاسم المستخدم للفرع الافتراضي.1 نقطة
-
run: [EL Config]: metadata: 2023-05-06 03:28:09.622--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.Neadypeaple] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.651--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.Person] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.658--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to one mapping element [field needypeople] is being defaulted to: class javaapplication26.Needypeople. [EL Config]: metadata: 2023-05-06 03:28:09.659--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to one mapping element [field adminstrator] is being defaulted to: class javaapplication26.Adminstrator. [EL Config]: metadata: 2023-05-06 03:28:09.665--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to many mapping element [field volunteerList] is being defaulted to: class javaapplication26.Volunteer. [EL Config]: metadata: 2023-05-06 03:28:09.667--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to one mapping element [field boardOfTrusts] is being defaulted to: class javaapplication26.BoardOfTrusts. [EL Config]: metadata: 2023-05-06 03:28:09.668--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.BoardOfTrusts] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.669--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to one mapping element [field person] is being defaulted to: class javaapplication26.Person. [EL Config]: metadata: 2023-05-06 03:28:09.67--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.Volunteer] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.67--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to one mapping element [field folders] is being defaulted to: class javaapplication26.Folders. [EL Config]: metadata: 2023-05-06 03:28:09.67--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to one mapping element [field person] is being defaulted to: class javaapplication26.Person. [EL Config]: metadata: 2023-05-06 03:28:09.671--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to one mapping element [field locality] is being defaulted to: class javaapplication26.Locality. [EL Config]: metadata: 2023-05-06 03:28:09.672--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to many mapping element [field campaignList] is being defaulted to: class javaapplication26.Campaign. [EL Config]: metadata: 2023-05-06 03:28:09.673--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to one mapping element [field campaign] is being defaulted to: class javaapplication26.Campaign. [EL Config]: metadata: 2023-05-06 03:28:09.677--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to many mapping element [field acceptList] is being defaulted to: class javaapplication26.Accept. [EL Config]: metadata: 2023-05-06 03:28:09.677--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.Material] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.682--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to one mapping element [field donations] is being defaulted to: class javaapplication26.Donations. [EL Config]: metadata: 2023-05-06 03:28:09.685--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.Needypeople] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.688--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to one mapping element [field person] is being defaulted to: class javaapplication26.Person. [EL Config]: metadata: 2023-05-06 03:28:09.689--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to one mapping element [field donationsID] is being defaulted to: class javaapplication26.Donations. [EL Config]: metadata: 2023-05-06 03:28:09.69--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.Money] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.69--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to one mapping element [field donations] is being defaulted to: class javaapplication26.Donations. [EL Config]: metadata: 2023-05-06 03:28:09.693--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.Donations] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.695--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to many mapping element [field needypeopleList] is being defaulted to: class javaapplication26.Needypeople. [EL Config]: metadata: 2023-05-06 03:28:09.696--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to many mapping element [field materialList] is being defaulted to: class javaapplication26.Material. [EL Config]: metadata: 2023-05-06 03:28:09.698--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to many mapping element [field moneyList] is being defaulted to: class javaapplication26.Money. [EL Config]: metadata: 2023-05-06 03:28:09.699--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to many mapping element [field campaignList] is being defaulted to: class javaapplication26.Campaign. [EL Config]: metadata: 2023-05-06 03:28:09.699--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to many mapping element [field acceptList] is being defaulted to: class javaapplication26.Accept. [EL Config]: metadata: 2023-05-06 03:28:09.699--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.Adminstrator] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.7--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to one mapping element [field localityname] is being defaulted to: class javaapplication26.Locality. [EL Config]: metadata: 2023-05-06 03:28:09.7--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to one mapping element [field person] is being defaulted to: class javaapplication26.Person. [EL Config]: metadata: 2023-05-06 03:28:09.702--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.Accept] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.702--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to one mapping element [field volunteer] is being defaulted to: class javaapplication26.Volunteer. [EL Config]: metadata: 2023-05-06 03:28:09.703--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to one mapping element [field donations] is being defaulted to: class javaapplication26.Donations. [EL Config]: metadata: 2023-05-06 03:28:09.704--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.Campaign] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.707--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to many mapping element [field donationsList] is being defaulted to: class javaapplication26.Donations. [EL Config]: metadata: 2023-05-06 03:28:09.707--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to one mapping element [field volunteer] is being defaulted to: class javaapplication26.Volunteer. [EL Config]: metadata: 2023-05-06 03:28:09.71--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to many mapping element [field volunteerList] is being defaulted to: class javaapplication26.Volunteer. [EL Config]: metadata: 2023-05-06 03:28:09.71--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.LocalityLocation] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.711--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the many to one mapping element [field locality] is being defaulted to: class javaapplication26.Locality. [EL Config]: metadata: 2023-05-06 03:28:09.714--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.Folders] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.715--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to many mapping element [field volunteerList] is being defaulted to: class javaapplication26.Volunteer. [EL Config]: metadata: 2023-05-06 03:28:09.716--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.Locality] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.718--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to many mapping element [field adminstratorList] is being defaulted to: class javaapplication26.Adminstrator. [EL Config]: metadata: 2023-05-06 03:28:09.718--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to many mapping element [field localityLocationList] is being defaulted to: class javaapplication26.LocalityLocation. [EL Config]: metadata: 2023-05-06 03:28:09.719--ServerSession(1463757745)--Thread(Thread[main,5,main])--The target entity (reference) class for the one to many mapping element [field volunteerList] is being defaulted to: class javaapplication26.Volunteer. [EL Config]: metadata: 2023-05-06 03:28:09.719--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.MoneyPK] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.72--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.LocalityLocationPK] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.721--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.VolunteerPK] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.721--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.CampaignPK] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.722--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.AcceptPK] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.723--ServerSession(1463757745)--Thread(Thread[main,5,main])--The access type for the persistent class [class javaapplication26.MaterialPK] is set to [FIELD]. [EL Config]: metadata: 2023-05-06 03:28:09.723--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.Neadypeaple] is being defaulted to: Neadypeaple. [EL Config]: metadata: 2023-05-06 03:28:09.742--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.Person] is being defaulted to: Person. [EL Config]: metadata: 2023-05-06 03:28:09.747--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.BoardOfTrusts] is being defaulted to: BoardOfTrusts. [EL Config]: metadata: 2023-05-06 03:28:09.749--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.Volunteer] is being defaulted to: Volunteer. [EL Config]: metadata: 2023-05-06 03:28:09.755--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.Material] is being defaulted to: Material. [EL Config]: metadata: 2023-05-06 03:28:09.756--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.Needypeople] is being defaulted to: Needypeople. [EL Config]: metadata: 2023-05-06 03:28:09.757--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.Money] is being defaulted to: Money. [EL Config]: metadata: 2023-05-06 03:28:09.757--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.Donations] is being defaulted to: Donations. [EL Config]: metadata: 2023-05-06 03:28:09.758--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.Adminstrator] is being defaulted to: Adminstrator. [EL Config]: metadata: 2023-05-06 03:28:09.758--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.Accept] is being defaulted to: Accept. [EL Config]: metadata: 2023-05-06 03:28:09.76--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.Campaign] is being defaulted to: Campaign. [EL Config]: metadata: 2023-05-06 03:28:09.761--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.LocalityLocation] is being defaulted to: LocalityLocation. [EL Config]: metadata: 2023-05-06 03:28:09.761--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.Folders] is being defaulted to: Folders. [EL Config]: metadata: 2023-05-06 03:28:09.763--ServerSession(1463757745)--Thread(Thread[main,5,main])--The alias name for the entity class [class javaapplication26.Locality] is being defaulted to: Locality. Exception in thread "main" Local Exception Stack: Exception [EclipseLink-30005] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.PersistenceUnitLoadingException Exception Description: An exception was thrown while searching for persistence archives with ClassLoader: sun.misc.Launcher$AppClassLoader@5c647e05 Internal Exception: javax.persistence.PersistenceException: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Predeployment of PersistenceUnit [JavaApplication26PU] failed. Internal Exception: Exception [EclipseLink-7220] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException Exception Description: The @JoinColumns on the annotated element [field campaign] from the entity class [class javaapplication26.Volunteer] is incomplete. When the source entity class uses a composite primary key, a @JoinColumn must be specified for each join column using the @JoinColumns. Both the name and the referencedColumnName elements must be specified in each such @JoinColumn. at org.eclipse.persistence.exceptions.PersistenceUnitLoadingException.exceptionSearchingForPersistenceResources(PersistenceUnitLoadingException.java:127) at org.eclipse.persistence.jpa.PersistenceProvider.createEntityManagerFactoryImpl(PersistenceProvider.java:107) at org.eclipse.persistence.jpa.PersistenceProvider.createEntityManagerFactory(PersistenceProvider.java:177) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:79) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54) at javaapplication26.JavaApplication26.main(JavaApplication26.java:28) Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Predeployment of PersistenceUnit [JavaApplication26PU] failed. Internal Exception: Exception [EclipseLink-7220] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException Exception Description: The @JoinColumns on the annotated element [field campaign] from the entity class [class javaapplication26.Volunteer] is incomplete. When the source entity class uses a composite primary key, a @JoinColumn must be specified for each join column using the @JoinColumns. Both the name and the referencedColumnName elements must be specified in each such @JoinColumn. at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.createPredeployFailedPersistenceException(EntityManagerSetupImpl.java:1954) at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:1945) at org.eclipse.persistence.internal.jpa.deployment.JPAInitializer.callPredeploy(JPAInitializer.java:98) at org.eclipse.persistence.jpa.PersistenceProvider.createEntityManagerFactoryImpl(PersistenceProvider.java:96) ... 4 more Caused by: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Predeployment of PersistenceUnit [JavaApplication26PU] failed. Internal Exception: Exception [EclipseLink-7220] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException Exception Description: The @JoinColumns on the annotated element [field campaign] from the entity class [class javaapplication26.Volunteer] is incomplete. When the source entity class uses a composite primary key, a @JoinColumn must be specified for each join column using the @JoinColumns. Both the name and the referencedColumnName elements must be specified in each such @JoinColumn. at org.eclipse.persistence.exceptions.EntityManagerSetupException.predeployFailed(EntityManagerSetupException.java:230) ... 8 more Caused by: Exception [EclipseLink-7220] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException Exception Description: The @JoinColumns on the annotated element [field campaign] from the entity class [class javaapplication26.Volunteer] is incomplete. When the source entity class uses a composite primary key, a @JoinColumn must be specified for each join column using the @JoinColumns. Both the name and the referencedColumnName elements must be specified in each such @JoinColumn. at org.eclipse.persistence.exceptions.ValidationException.incompleteJoinColumnsSpecified(ValidationException.java:1847) at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.getJoinColumnsAndValidate(MappingAccessor.java:757) at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.getJoinColumns(MappingAccessor.java:657) at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ObjectAccessor.processForeignKeyRelationship(ObjectAccessor.java:663) at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ObjectAccessor.processOwningMappingKeys(ObjectAccessor.java:733) at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ManyToOneAccessor.process(ManyToOneAccessor.java:123) at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processOwningRelationshipAccessors(MetadataProject.java:1580) at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processStage3(MetadataProject.java:1833) at org.eclipse.persistence.internal.jpa.metadata.MetadataProcessor.processORMMetadata(MetadataProcessor.java:580) at org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor.processORMetadata(PersistenceUnitProcessor.java:585) at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:1869) ... 6 more BUILD FAILED (total time: 1 second)1 نقطة
-
1 نقطة
-
قم باعداد برنامج بلغه بايثون يقوم بعمل ايميل رسمى للطالب1 نقطة
-
1 نقطة
-
1- كتابة الخوارزمية ورسم خريطة التدفق لبرنامج يطبع الاعداد الزوجية من 0 الئ 20 2 - كتابة ااخوارزمية ورسم خريطه التدفق لبرنامج يطبع مضاعفات العدد7 من 1 الئ 301 نقطة
-
هذا الكود الخاص بي هل اجد احد يساعدني بتعديل الكود لعمل بشكل صحيح وايجاد run المطلوب #include <stdio.h> int main() { int n, i, count = 0; float price, total = 0, discount = 0, max_discount = 0; scanf("%d", &n); for(i = 0; i < n; i++) { scanf("%f", &price); total += price; } switch(n) { case 1: case 2: if(total < 20) discount = 0; else discount = total * 0.1; break; default: if(total < 20) discount = total * 0.07; else { discount = total * 0.15; if(discount > total * 0.5) discount = total * 0.5; } break; } for(i = 0; i < n; i++) { scanf("%f", &price); if(price > 7) discount += total * 0.01; } printf("%.2f\n", total - discount); return 0; } حيث ان مطلوب بلغة c1 نقطة
-
يبدو أن المشكلة هي في استخدام الدالة setInterval مع الدالة setTimer. تحتوي الدالة setInterval على تأخير في تنفيذ الكود الذي يتم تمريره لها، والذي قد يؤدي إلى تغيير قيمة timer عدة مرات بدلاً من مرة واحدة فقط. يمكن تجنب هذه المشكلة عن طريق استخدام دالة العد التنازلي setTimeout بدلاً من setInterval. يمكنك استخدام الكود التالي كبديل: const [timer, setTimer] = useState(0) const format = (time) => { let hours = Math.floor((time / 60 / 60) % 24); let minutes = Math.floor((time / 60) % 60); let secondes = Math.floor(time % 60); hours = hours < 10 ? "0" + hours : hours; minutes = minutes < 10 ? "0" + minutes : minutes; secondes = secondes < 10 ? "0" + secondes : secondes; return hours + ":" + minutes + ":" + secondes; }; const incrementTimer = () => { setTimer(timer + 1); setTimeout(incrementTimer, 2000); // تمديد الوقت بـ 2 ثواني }; useEffect(() => { setTimeout(incrementTimer, 2000); }, []); يقوم الكود الجديد بإنشاء دالة incrementTimer تقوم بزيادة الوقت بمقدار ثانية واحدة ، ثم تستدعي نفسها باستخدام setTimeout بتأخير يبلغ 2 ثوانٍ. يتم استدعاء incrementTimer مرة واحدة فقط في useEffect بمجرد تحميل الصفحة. وبالتالي، سيتم زيادة الوقت بمقدار ثانية واحدة كل ثانيتين بدلاً من كل ثانية واحدة كما كان في الكود الأصلي.1 نقطة
-
عند استخدام الدالة setInterval ، يتم استدعاؤها بشكل متكرر كل 1000 مللي ثانية (1 ثانية) وتقوم بتحديث حالة الـ timer. ومع ذلك، يمكن أن يحدث تأخير في عملية تحديث الحالة، وهذا يؤدي إلى تجاوز قيمة timer المرتقبة بشكل خاطئ. لذلك باستطاعتك استخدام دالة الـ useEffect لتحديث قيمة الـ timer بشكل دقيق بعد انتهاء الـ interval. const [timer, setTimer] = useState(0); const format = (time) => { let hours = Math.floor((time / 60 / 60) % 24); let minutes = Math.floor((time / 60) % 60); let secondes = Math.floor(time % 60); hours = hours < 10 ? "0" + hours : hours; minutes = minutes < 10 ? "0" + minutes : minutes; secondes = secondes < 10 ? "0" + secondes : secondes; return hours + ":" + minutes + ":" + secondes; }; useEffect(() => { const interval = setInterval(() => { setTimer((timer) => timer + 1); }, 1000); return () => { clearInterval(interval); }; }, []); useEffect(() => { const timerValue = format(timer); // Do something with the timerValue, like displaying it }, [timer]); من خلال إضافة دالة useEffect جديدة والتي تتم استدعائها عند تحديث الـ timer يتم إنشاء متغير يحتوي على القيمة المنسقة للـ timer باستخدام دالة format، وعليك باستخدام هذا المتغير لعرض الـ timer بالشكل المطلوب. ولاحظ أن دالة clearInterval مفادها هو إلغاء الـ interval بشكل صحيح عندما يتم إيقاف تشغيل المؤشر، وإعادة ترتيب الكود عن طريق نقل دالة setInterval إلى داخل دالة useEffect. وإحدى الطرق الأفضل هي استخدام دالة useRef لحفظ قيمة الـ interval والسماح لنا بإيقافها بشكل دقيق وفي الوقت المناسب، بالشكل التالي: import { useState, useEffect, useRef } from "react"; function Timer() { const [timer, setTimer] = useState(0); const intervalRef = useRef(); const format = (time) => { let hours = Math.floor((time / 60 / 60) % 24); let minutes = Math.floor((time / 60) % 60); let secondes = Math.floor(time % 60); hours = hours < 10 ? "0" + hours : hours; minutes = minutes < 10 ? "0" + minutes : minutes; secondes = secondes < 10 ? "0" + secondes : secondes; return hours + ":" + minutes + ":" + secondes; }; useEffect(() => { intervalRef.current = setInterval(() => { setTimer((timer) => timer + 1); }, 1000); return () => { clearInterval(intervalRef.current); }; }, []); const stopTimer = () => { clearInterval(intervalRef.current); }; const resetTimer = () => { setTimer(0); }; return ( <div> <div>{format(timer)}</div> <button onClick={stopTimer}>Stop Timer</button> <button onClick={resetTimer}>Reset Timer</button> </div> ); } تستخدم دالة useRef لإنشاء مرجع ref جديد لـ interval وحفظ قيمته في متغير، وإرجاع الدالة useEffect التي تستخدم intervalRef.current بدلاً من interval وإعادة تعيين قيمة intervalRef.current عندما يتم تحديث الـ timer. كما تم إضافة دالة stopTimer لإلغاء الـ interval و إنشاء دالة resetTimer لإعادة الـ timer إلى القيمة الافتراضية. وإضافة زر "Stop Timer" لإيقاف تشغيل المؤشر وزر "Reset Timer" لإعادة تعيين الـ timer إلى القيمة الافتراضية، وتستطيع استخدام الـ format() في عرض الـ timer بالشكل المطلوب.1 نقطة
-
إذا قمت برفع المشروع بشكل يدوي على Netlify أي من خلال رفع مجلد المشروع نفسه وليس من خلال مستودع GitHub، فستحتاج إلى إعادة رفع المشروع من جديد في كل مرة تقوم بتغيير الكود الخاص بالمشروع. ولذلك الحل هو رفع المشروع عن طريق مستودع GitHub: بعد تسجيل الدخول، يجب عليك النقر على الزر "New site from Git" لإنشاء موقع جديد. ستحتاج إلى السماح لـ Netlify بالوصول إلى حساب GitHub الخاص بك. حدد مستودعك (repository) في القائمة المنسدلة. بعد ذلك، يمكنك النقر على الزر "Deploy site" لنشر مشروعك. بعد الانتهاء من النشر، سيتم توفير رابط URL لمشروعك على Netlify. يمكنك النقر عليه لعرض التطبيق الخاص بك. والآن عند عمل أي تغييرات على الكود ونشرها على المستودع من خلال الأمر push سيتم عمل redeploy أي إعادة نشر للموقع بالتغييرات الجديدة دون تدخل منك. وأيضًا تستطيع ربط المشروع الحالي بمستودع GitHub من خلال الضغط على Site settings في Netlify ثم إختيار Build & deploy ثم إختيار Link repository. بخصوص الظهور على محركات البحث و تحسين الـ SEO أولاً عليك بإنشاء حساب على Google search console ثم إضافة موقعك لكي يظهر على جوجل بشكل سريع وتراقب الترافيك والبيانات الخاص به، وستظهر بترتيب أعلى في جوجل. ومن الأفضل أيضًا توفير sitemap.xml على الموقع حتى يتمكن Google من العثور على جميع الصفحات والتحديثات. وبمجرد التسجيل وإضافة رابط موقعك وتأكيد أنك مالك الموقع، سيظهر لك نافذة البيانات كالتالي، وعليك بإرسال طلب بأرشفة موقعك من خلال الضغط على URL Inspection ثم وضع رابط موقعك ثم إختيار Request indexing. وانتظر فترة 48 ساعة وسيظهر موقعك وتستطيع البحث عنه باسم الموقع في جوجل أو الرابط.1 نقطة
-
يبدو أن الحزمة munafio/chatify تحتاج إلى حزمة pusher/pusher-php-server كحزمة تبعية بإصدارات محددة، ولكن الملف الموجود في المشروع الحالي يحتاج إلى إصدارات مختلفة من هذه الحزمة. لحل هذه المشكلة، يمكنك إما تثبيت الإصدارات الصحيحة من pusher/pusher-php-server المطلوبة في ملف composer.json الخاص بمشروعك، أو تحديد نطاق متوافق في ملف composer.json مثل "^3.4@dev"، "^7.0"، أو "^7.2" والتي تشمل الإصدارات المطلوبة. بعد ذلك، قم بتشغيل composer require munafio/chatify مرة أخرى لتثبيت الحزمة بنجاح. وكحل افضل، دع composer يقوم بتثبيت الاصدار المناسب من حزمة munafio/chatify الملائم لنسخة pusher-php-server المثبتة لديك: composer require munafio/chatify --with-all-dependencies او: composer require munafio/chatify --W1 نقطة
-
الخطأ يظهر أن هناك تعارض في متطلبات الحزم المستخدمة في مشروعك. وتحتاج إلى التحقق من ملف composer.json الخاص بمشروع Laravel الخاص بك والتأكد من أن متطلبات الحزم محدثة وتتوافق مع متطلبات حزمة Chatify. في هذه الحالة، تحتاج إلى تحديث متطلبات الحزم في ملف composer.json لتتوافق مع متطلبات حزمة Chatify، و يمكنك القيام بذلك عن طريق تنفيذ الأمر التالي في مجلد مشروع Laravel الخاص بك: composer require pusher/pusher-php-server:^7.0 ومن ثم، يمكنك تنفيذ أمر تثبيت Chatify مرة أخرى باستخدام الأمر التالي: composer require munafio/chatify وإذا واجهت أي مشاكل أخرى، فيمكنك استخدام الخيار --with-all-dependencies للسماح بالتحديثات والإزالات للحزم التي تم تأمينها على إصدارات محددة. composer require munafio/chatify --with-all-dependencies أيضً تستطيع ا تحديد الإصدار المحدد لحزمة Chatify بإضافة القيود الخاصة بالإصدار في الأمر composer require: composer require munafio/chatify:^1.6.1 بعد تحديث متطلبات الحزم وتثبيت Chatify بنجاح، تأكد من تنفيذ الأمر التالي لتحديث ملف الارتباطات composer.lock: composer update وفي النهاية، تأكد من إضافة مزود Pusher إلى ملف تكوين Laravel الخاص بك config/app.php كما هو موضح في توثيق Chatify.1 نقطة
-
في Python ، الدالة المجهولة هي دالة يتم تعريفها بدون اسم. بينما يتم تحديد الدوال العادية باستخدام الكلمة الأساسية def في Python ، يتم تحديد الوظائف المجهولة باستخدام الكلمة الأساسية lambda. ومن ثم ، فإن الدوال المجهولة تسمى أيضًا وظائف لامدا. كيف تستخدم دوال لامدا في بايثون؟ دالة lambda في لغة python لها الصيغة التالية. lambda arguments: expression يمكن أن تحتوي دوال Lambda على أي عدد من الوسيطات ولكن تحتوي على تعبير واحد فقط. يتم تقييم التعبير وإعادته. يمكن استخدام وظائف Lambda أينما كانت الكائنات الوظيفية مطلوبة. مثال على دالة Lambda في لغة بايثون : فيما يلي مثال على دالة lambda التي تضاعف قيمة الإدخال. #برنامج لإظهار استخدام دالة لامدا double = lambda x: x * 2 print(double(5)) الإخراج 10 في البرنامج أعلاه ، lambda x: x * 2 هي وظيفة lambda. هنا x هي الوسيطة و x * 2 هي التعبير الذي يتم تقييمه وإعادته. هذه الدالة ليس لها اسم. تقوم بإرجاع كائن الوظيفة الذي تم تعيينه لمضاعفة المعرف. يمكننا الآن تسميتها دالة عادية. double = lambda x: x * 2 هو تقريبا مثل : def double(x): return x * 2 استخدامات دالة Lambda في لغة بايثون : نستخدم دوال لامدا عندما نحتاج إلى وظيفة غير معروفة لفترة قصيرة من الزمن. في بايثون ، نستخدمها عمومًا كوسيطة لوظيفة ذات ترتيب أعلى (وظيفة تأخذ وظائف أخرى كوسائط). تُستخدم وظائف Lambda جنبًا إلى جنب مع الوظائف المضمنة مثل ()filter() , map .. وما إلى ذلك. مثال إستخدام lamda مع filter : # Program to filter out only the even items from a list my_list = [1, 5, 4, 6, 8, 11, 3, 12] new_list = list(filter(lambda x: (x%2 == 0) , my_list)) print(new_list) [4, 6, 8, 12] # output1 نقطة
-
ميزات تعابير lambda: دوال نكتبها بشكل مختصر lambda arguments : expression ليس لها اسم >>> def identity(x): ... return x يكافيء >>> lambda x: x يمكنها أخذ أي عدد من الوسطاء، وتعبير واحد expression x = lambda a, b : a * b print(x(5, 6)) x = lambda a, b, c : a + b + c print(x(5, 6, 2)) يمكن تمريرها كمعامل لدوال height order functions def myfunc(n): return lambda a : a * n my_doubler = myfunc(2) my_tripler = myfunc(3) print(my_doubler(10)) print(my_tripler(10)) تابع توثيق حسوب: Python/lambda_expressions يوجد أمثلة عنها في1 نقطة
-
سنأخذ مثال سلسلة الأدوات الذي أنشأناه في المقال السابق، ثم نضيفه لنتمكن من نشر تطبيقنا، إذ سنرفع الشيفرة على GitHub، وننشر التطبيق باستخدام Netlify، وسنوضح كيفية تطبيق اختبار بسيط عليه. المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت. الهدف: إنهاء العمل من خلال دراسة الحالة الكاملة لسلسلة الأدوات مع التركيز على مرحلة نشر التطبيق. مرحلة ما بعد التطوير يُحتَمل أن تواجه العديد من المشاكل التي يجب حلها في هذه المرحلة من دورة حياة المشروع، لذلك يجب إنشاء سلسلة أدوات تعالج هذه المشاكل بطريقة تتطلب أقل قدر ممكن من التدخل اليدوي. إليك بعض الأشياء التي يجب مراعاتها في المشروع: إنشاء بنية إنتاج: ضمان تصغير الملفات وتقسيمها وتطبيق تقنية هز الشجرة وتعطيل ذاكرة الإصدارات المخبئية Cache Busted للمتصفح. تشغيل الاختبارات: يمكن أن تتراوح من "هل نُسِّقت هذه الشيفرة تنسيقًا صحيحًا؟" إلى "هل يطبّق هذا الشيء ما هو متوقع منه؟"، والتأكد من أن الاختبارات الفاشلة تمنع النشر. نشر الشيفرة المحدثة فعليًا إلى عنوان URL مباشر: أو عنوان URL مرحلي لمراجعتها أولًا. كما تنقسم المهام السابقة إلى مهام أخرى، لأن معظم فرق تطوير الويب لها شروطها وعملياتها الخاصة لجزء من مرحلة ما بعد التطوير على الأقل. سنستخدم في مشروعنا عرض الاستضافة الثابت من Netlify لاستضافة مشروعنا. تمنحنا Netlify استضافة أو عنوان URL لعرض المشروع عبر الإنترنت ومشاركته مع أصدقائك وعائلتك وزملائك. يكون النشر على الاستضافة في نهاية دورة حياة المشروع، ولكن تعمل خدمات مثل Netlify على خفض تكلفة النشر من الناحية المالية والوقت المطلوب للنشر الفعلي، إذ يمكن النشر أثناء التطوير إلى مشاركة العمل أو الحصول على إصدار تجريبي لأغراض الأخرى. تتيح خدمة Netlify تشغيل مهام ما قبل النشر، وهو ما يعني في حالتنا أنه يمكن تنفيذ جميع عمليات إنشاء شيفرة الإنتاج ضمن Netlify وإذا نجح الإصدار، فستُنشَر تغييرات موقع الويب. تقدّم Netlify خدمة النشر بالسحب والإفلات Drag and Drop Deployment Service، لكننا نعتزم بدء نشر جديد إلى Netlify في كل مرة نرفع فيها الشيفرة على مستودع GitHub. إنها بالضبط أنواع الخدمات المتصلة التي نشجعك على البحث عنها عند اتخاذ قرار بشأن سلسلة أدوات البناء الخاصة بك. يمكننا رفع شيفرتنا على GitHub، وستشغّل الشيفرة المحدثة تلقائيًا منهج البناء الكامل. إذا كان كل شيء على ما يرام، فسيُنشَر التغيير المباشر تلقائيًا، لكن الإجراء الوحيد الذي نحتاجه هو الرفع الأولي. عملية البناء بما أننا نستخدم Parcel للتطوير، فإن خيار البناء سهل الإضافة. يمكننا تشغيل الخادم باستخدام الأمر npx parcel build src/index.html بدلًا من الأمر npx parcel src/index.html، وستبني Parcel كل شيء جاهزًا للإنتاج بدلًا من تشغيله لأغراض التطوير والاختبار فقط، ويتضمن ذلك تصغير الشيفرة وتطبيق تقنية هز الشجرة عليها، وتعطيل الذاكرة المخبئية على أسماء الملفات. تُوضَع شيفرة الإنتاج التي أنشأناها في دليل جديد يسمى dist يحتوي على جميع الملفات المطلوبة لتشغيل موقع الويب، ويكون جاهزًا للتحميل على الخادم. ليس تطبيق هذه الخطوة يدويًا هدفنا النهائي، بل نريد أن يحدث البناء تلقائيًا وأن تُنشَر نتيجة الدليل dist مباشرة على موقعنا على الإنترنت. يجب إعداد شيفرتنا وGitHub وNetlify للتواصل مع بعضها بعضًا، ليكتشف Netlify التغييرات تلقائيًا ويشغّل مهام البناء ويصدر تحديثًا جديدًا في كل مرة نحدّث فيها مستودع شيفرة GitHub. سنضيف أمر البناء إلى الملف package.json بوصفه سكربت npm، ليشغّل الأمر npm run build عملية البناء. ليست هذه الخطوة ضرورية، لكنها أفضل ممارسة جيدة لعادة الإعداد في جميع المشاريع، ثم يمكننا الاعتماد على الأمر npm run build لتطبيق خطوة البناء الكاملة، دون الحاجة إلى تذكر وسطاء أمر البناء المحدَّدة لكل مشروع. افتح الملف package.json في الدليل الجذر لمشروعك، وابحث عن الخاصية scripts. سنضيف الأمر build الذي يمكننا تشغيله لبناء شيفرتنا. أضف السطر التالي إلى مشروعك: "scripts": { ... "build": "parcel build src/index.html" } ملاحظة: إذا احتوت الخاصية scripts على أمر ضمنها، فضع فاصلة في نهايتها حسب صيغة JSON. يجب أن تكون الآن قادرًا على تشغيل الأمر التالي في جذر دليل مشروعك لتشغيل خطوة بناء الإنتاج، ولكن أنهِ أولًا عملية التشغيل باستخدام الاختصار Ctrl + C: npm run build يكون خرج الأمر السابق كما يلي، ويوضح هذا الخرج ملفات الإنتاج المُنشَأة، وحجمها، والمدة التي استغرقتها للبناء: dist/src.99d8a31a.js.map 446.15 KB 63ms dist/src.99d8a31a.js 172.51 KB 5.55s dist/stars.7f1dd035.svg 6.31 KB 145ms dist/asteroid2.3ead4904.svg 3.51 KB 155ms dist/asteroid1.698d75e9.svg 2.9 KB 153ms dist/src.84f2edd1.css.map 2.57 KB 3ms dist/src.84f2edd1.css 1.25 KB 1.53s dist/bg.084d3fd3.svg 795 B 147ms dist/index.html 354 B 944ms يجب استضافة شيفرة المشروع في مستودع git الخاص بك لتتمكّن من إنشاء نسخة منه. خطوتنا التالية هي رفع المشروع على GitHub. تنفيذ التغييرات على GitHub سيساعدك هذا القسم على تجاوز حدود تخزين شيفرتك في مستودع git، ولكنك لن تتعلّم git بالتفصيل. هيّأنا دليل العمل بوصفه دليل عمل git سابقًا، وهناك طريقة سريعة للتحقق من ذلك وهي تشغيل الأمر التالي: git status يجب أن تحصل على تقرير بحالة الملفات المُتتبَّعة والملفات المُنظَّمة وما إلى ذلك، وتُعَد هذه المصطلحات جزءًا من قواعد git. إذا حصلتَ على الخطأ fatal: not a git repository، فهذا يدل على أن دليل العمل ليس دليل عمل git، وبالتالي يجب تهيئة git باستخدام الأمر git init. أمامنا الآن ثلاث مهام وهي: إضافة التغييرات التي أجريناها إلى مكان يدعَى stage، وهو اسم خاص بالمكان الذي يودع git الملفات فيه. تنفيذ التغييرات على المستودع. رفع التغييرات على GitHub. أولًا، يمكنك إضافة التغييرات من خلال تشغيل الأمر التالي: git add . لاحظ النقطة في النهاية التي تعني "كل شيء في هذا الدليل". يشبه الأمر git add . إلى حدٍ ما نهج المطرقة، إذ سيضيف جميع التغييرات المحلية التي عملت عليها دفعة واحدة. إن أردت تحكمًا أفضل فيما تضيفه، فاستخدم الأمر git add -p للعمليات التفاعلية، أو أضف ملفات باستخدام الأمر git add path/to/file. ثانيًا، أصبحت الآن الشيفرة منظمة، ويمكننا تثبيت التغيير من خلال تشغيل الأمر التالي: git commit -m ’committing initial code’ ثالثًا، أخيرًا، يجب رفع الشيفرة على مستودع GitHub المستضاف. يمكنك زيارة الصفحة new في موقع github لإنشاء مستودعك لاستضافة هذه الشيفرة. رابعًا، امنح مستودعك اسمًا قصيرًا يسهل تذكره بدون مسافات (استخدم الشرطات لفصل الكلمات)، واكتب وصفًا مناسبًا، ثم انقر على زر إنشاء مستودع Create Repository في أسفل الصفحة. يجب أن يكون لديك الآن عنوان URL بعيد يؤشّر إلى مستودع GitHub الجديد الخاص بك. خامسًا، يجب إضافة هذا الموقع البعيد إلى مستودع git المحلي قبل أن نتمكن من رفعه هناك، وإلا فلن يتمكن من العثور عليه. يجب تشغيل أمر له البنية التالية (استخدم خيار HTTPS المُقدَّم حاليًا وليس خيار SSH، خاصة إذا كنت جديدًا على GitHub): git remote add github https://github.com/yourname/repo-name.git لذلك إذا كان عنوان URL البعيد الخاص بك هو https://github.com/remy/super-website.git -كما في لقطة الشاشة أعلاه- فسيكون الأمر كما يلي: git remote add github https://github.com/remy/super-website.git غيّر عنوان URL إلى المستودع الخاص بك، وشغّل الأمر. سادسًا، أصبحنا الآن جاهزين لرفع شيفرتنا على GitHub، ويمكنك الآن تشغيل الأمر التالي: git push github main سيُطلب منك الآن إدخال اسم مستخدم وكلمة مرور قبل أن يسمح Git بإرسال الرفع، لأننا استخدمنا خيار HTTPS بدلًا من خيار SSH كما رأينا سابقًا. لذلك تحتاج إلى اسم مستخدم Github الخاص بك وكلمة مرور -إن لم تكن المصادقة الثنائية Two-Factor Authentication -أو 2FA اختصارًا- مفعّلة فإننا نشجعك دائمًا على تفعيلها، ولكن ضع في بالك أنك إذا فعلتها فستحتاج لاستخدام رمز وصول شخصي بجانب كلمة السر الخاصة بالحساب. تحتوي صفحات المساعدة على Github على إرشادات بسيطة وممتازة تغطي كيفية الحصول على هذا الرمز. ملاحظة: إذا كنت مهتمًا باستخدام خيار SSH، وبالتالي تجنب الحاجة إلى إدخال اسم المستخدم وكلمة المرور في كل مرة ترفع فيها شيفرة على GitHub، فيمكنك الاطلاع على فيديو الاتصال بخدمة GitHub دون كلمة سر. يوجّه الأمر السابق git لرفع الشيفرة -أو ما يسمى بالنشر- على الموقع البعيد الذي أطلقنا عليه اسم github -وهو المستودع المستضاف على github.com ويمكننا أن نطلق عليه أي اسم نريده- باستخدام الفرع main. لم ننشئ أي فروع إضافية على هذا المشروع الإطلاق، ولكن الفرع main هو الفرع الافتراضي لعملنا وهو ما يؤسسه git بصورة افتراضية، وهو أيضًا الفرع الافتراضي الذي سيبحث عنه Netlify. الخطوة التالية في سلسلة الأدوات هي توصيل GitHub مع Netlify لنشر مشروعنا مباشرة على الويب. استخدام Netlify للنشر يُعَد النشر من GitHub إلى Netlify أمرًا بسيطًا بمجرد معرفة الخطوات، خاصة مع مواقع الويب الثابتة Static Websites مثل مشروعنا. انتقل إلى صفحة البداية في Netlify. اضغط على زر Github أسفل عنوان النشر المستمر Continuous Deployment الذي يعني أنه كلما تغير مستودع الشيفرة، فسيحاول Netlify نشرها، وبالتالي فهي مستمرة. يمكن أن تحتاج إلى ترخيص Netlify مع GitHub اعتمادًا على ما إذا أعطيت Netlify ترخيصًا من قبل، واختيار الحساب الذي تريد إعطاءه ترخيصًا، إذا كان لديك عدة حسابات أو مؤسسات على GitHub. اختر الحساب الذي رفعت مشروعك عليه. سيطالبك Netlify بقائمة من مستودعات GitHub التي يمكنك العثور عليها. حدد مستودع مشروعك وانتقل إلى الخطوة التالية. بما أننا ربطنا Netlify بحساب Github وأعطيناه إذن الوصول لنشر مستودع المشروع، فسيسأل Netlify عن كيفية إعداد المشروع للنشر وما الذي يجب نشره. يجب إدخال الأمر npm run build وتحديد الدليل dist لدليل النشر الذي يحتوي على الشيفرة التي نريد جعلها عامة. انقر على نشر الموقع Deploy site في النهاية. يجب أن تحصل بعد انتظار قصير لحدوث النشر على عنوان URL يمكنك الانتقال إليه لرؤية موقعك المنشور. إذا أجريت تغييرًا ورفعت التغيير إلى مستودع git البعيد على GitHub، فسيؤدي ذلك إلى إرسال إشعار إلى Netlify الذي سيشغّل مهمة البناء ثم ينشر دليل dist الناتج على موقعنا المنشور. جرّب إجراء تغيير بسيط على تطبيقك، ثم ارفعه إلى GitHub باستخدام الأوامر التالية: git add . git commit -m ‘simple netlify test’ git push github main يجب أن ترى تحديث موقعك المنشور بالتغيير. يستغرق ذلك بضع دقائق للنشر، لذا تحلى بالصبر. يمكننا اختياريًا تغيير اسم مشروع Netlify أو تحديد استخدام اسم نطاقنا الذي يقدّم Netlify بعض الوثائق الممتازة عنه. الاختبار يُعَد الاختبار بحد ذاته موضوعًا واسعًا حتى في مجال تطوير الواجهة الأمامية. سنوضح كيفية إضافة اختبار أولي إلى مشروعك وكيفية استخدام الاختبار للسماح بنشر المشروع أو منعه في حال وجود مشاكل. هناك طرق متعددة للتعامل مع مشاكل الاختبارات هي: الاختبار الشامل End-to-end Testing: يتضمن نقر الزائر على شيء ما مع حدوث بعض الأمور الأخرى. اختبار التكامل Integration Testing: يتضمن هذا الاختبار السؤال: "هل ستستمر بالعمل إحدى كتل الشيفرة بطريقة صحيحة عند اتصالها بكتلة أخرى؟" اختبار الوحدة Unit Testing: تُختبَر أجزاء صغيرة ومحددة من الوظائف لمعرفة ما إذا كانت تفعل ما يفترض منها فعله. تذكّر أيضًا أن الاختبارات لا تقتصر على شيفرة جافاسكربت، إذ يمكن تشغيل الاختبارات على DOM المُصيَّر، وتفاعلات المستخدم، وCSS، وحتى على مظهر الصفحة. سننشئ اختبارًا صغيرًا في مشروعنا يتحقق من بيانات وكالة ناسا التابعة لجهة خارجية والتأكد من أنها في التنسيق الصحيح. إذا لم يكن الأمر كذلك، فسيفشل الاختبار وسيمنع المشروع من العمل. الاختبار موضوع ضخم يتطلب مقالات منفصلة خاصة به، ولكننا نأمل أن يجعلك هذا القسم على الأقل مدركًا لأهمية الاختبار. لا يتضمن اختبار هذا المشروع إطارًا اختباريًا، إلا أن هناك عددًا كبيرًا من خيارات إطار العمل. ليس الاختبار مهمًا بحد ذاته، فالمهم هو كيفية التعامل مع فشل أو نجاح الاختبار. ستتضمن بعض منصات النشر طريقة محددة للاختبار كجزء من سير العمل الخاصة بها. تدعم جميع المنتجات مثل GitHub وGitLab إجراء الاختبارات على التنفيذات. بما أننا ننشر مشروعنا على Netlify الذي لا يسأل إلا عن أمر البناء، فسيتعين علينا جعل الاختبارات جزءًا من عملية البناء. إذا فشل الاختبار، فسيفشل البناء، ولن ينشر Netlify. أولًا، انتقل إلى الملف package.json وافتحه. ثانيًا، ابحث عن الخاصية scripts وحدّثها لتحتوي على أوامر البناء والاختبار التالية: "scripts": { … "test": "node tests/*.js", "build": "npm run test && parcel build src/index.html" } ثالثًا، يجب الآن إضافة الاختبار إلى قاعدة شيفرتنا. أنشئ دليلًا جديدًا في الدليل الجذر الخاص بك وسمِّه tests: mkdir tests رابعًا، أنشئ ملف اختبار ضمن الدليل الجديد: cd tests touch nasa-feed.test.js خامسًا، افتح هذا الملف وأضف محتويات الملف nasa-feed.test.js إليه. سادسًا، يستخدم هذا الاختبار حزمة axios لجلب البيانات التي نريد اختبارها. شغّل الأمر التالي لتثبيت هذه الاعتمادية: npm install --save-dev axios يجب تثبيت axios يدويًا لأن Parcel لن تساعدنا فيها. تقع اختباراتنا خارج نطاق رؤية Parcel في نظامنا، نظرًا لأن Parcel لا ترى أو تدير أيًا من شيفرة الاختبار، لذلك يجب تثبيت الاعتمادية بأنفسنا. سابعًا، يمكننا تشغيل الأمر التالي في سطر الأوامر لإجراء الاختبار يدويًا: npm run test إذا نجحت عملية الاختبار، فالنتيجة هي لا شيء، وهذا يُعَد نجاحًا بحد ذاته. كما جرى الخروج من الاختبار بإشارة خاصة تخبر سطر الأوامر بأن الاختبار ناجح، وتكون قيمة إشارة الخروج 0، وإذا كان هناك فشل، فسيفشل الاختبار مع رمز الخروج 1، وهي قيمة على مستوى النظام تدل على حدوث فشل شيء ما. يستخدم الأمر npm run test لغة Node.Js لتشغيل جميع الملفات الموجودة في دليل الاختبارات التي تنتهي بالامتداد .js. يُستدعَى الأمر npm run test في سكربت البناء، ثم سترى السلسلة && التي تعني أنه "إذا نجح الشيء الموجود على اليسار (الخرج صفر)، فافعل الشيء الموجود على اليمين"، أي إذا نجحت الاختبارات، فطبّق بناء الشيفرة. ثامنًا، يجب رفع الشيفرة الجديدة إلى GitHub باستخدام أوامر التالية المماثلة لما استخدمته سابقًا: git add . git commit -m ‘adding test’ git push github main يمكن أن ترغب في بعض الأحيان في اختبار نتيجة شيفرة البناء، لأنها ليست الشيفرة الأصلية التي كتبناها، لذلك يجب تشغيل الاختبار بعد أمر البناء. أخيرًا، سينشر Netlify تحديث المشروع بعد دقيقة أو نحو ذلك من الرفع، إذا اجتاز الاختبار فقط. الخلاصة لا يزال هناك طريق طويل لقطعه قبل أن تتمكن من عَدّ نفسك ممتازًا في استخدام الأدوات من طرف العميل، لكن نأمل أن تكون هذه السلسلة من المقالات منحتك أول خطوة مهمة نحو فهم هذه الأدوات. لنلخص جميع أجزاء سلسلة الأدوات: تُطبَّق جودة الشيفرة وصيانتها بواسطة الأداتين Eslint وPrettier، وتُضاف هذه الأدوات بوصفها اعتماديات تطوير devDependencies إلى المشروع عبر الأمر npm install --dev eslint prettier eslint-plugin-react. كما يجب استخدام إضافة Eslint لأن المشروع يستخدم React. هناك نوعان من ملفات الإعداد التي تقرأها أدوات جودة الشيفرة هما: .eslintrc و.prettierrc. نستخدم أداة Parcel أثناء التطوير للتعامل مع الاعتماديات. يعمل parcel src/index.html في الخلفية لمراقبة التغييرات وبناء الشيفرة المصدرية تلقائيًا. يُعالَج النشر عن طريق رفع التغييرات إلى Github في الفرع main، مما يؤدي إلى البناء والنشر على Netlify لنشر المشروع. يكون عنوان URL في مثالنا هو near-misses.netlify.com، وسيكون لديك عنوان URL الفريد الخاص بك. كما يوجد اختبار بسيط يمنع بناء ونشر الموقع إذا لم تعطنا NASA API تنسيق البيانات الصحيح. هذا المقال جزء من سلسلة مقالات بعنوان تعلم تطوير الويب والتي تشرح كامل عملية تطوير الويب من واجهات أمامية وخلفية بالكامل. ترجمة -وبتصرُّف- للمقال Deploying our app. اقرأ أيضًا المقال السابق: بناء نموذج كامل لسلسلة أدوات تطوير الويب من طرف العميل أساسيات بناء تطبيقات الويب كيفية نشر تطبيق Rails باستخدام AZK1 نقطة
-
وأنت تكتب الشيفرة، ستقول في نفسك «أريد تشغيل هذه الدالة بعد قليل وليس الآن الآن. هذا ما نسمّيه "بجدولة الاستدعاءات" (scheduling a call). إليك تابِعين اثنين لهذه الجدولة: يتيح لك setTimeout تشغيل الدالة مرّة واحدة بعد فترة من الزمن. يتيح لك setInterval تشغيل الدالة تكراريًا يبدأ ذلك بعد فترة من الزمن ويتكرّر كلّ فترة حسب تلك الفترة التي حدّدتها. صحيح أنّ هذين التابِعين ليسا في مواصفة لغة جافاسكربت إلّا أنّ أغلب البيئات فيها مُجدوِل داخلي يقدّمهما لنا. وللدقّة، فكلّ المتصّفحات كما وNode.js تدعمهما. تابع تحديد المهلة setTimeout الصياغة: let timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...) المُعاملات: func|code: ما يجب تنفيذه أكان دالة أو سلسلة نصية فيها شيفرة. عادةً هي دالة ولكن كعادة الأسباب التاريخية (أيضًا) يمكن تمرير سلسلة نصية فيها شيفرة، ولكنّ ذلك ليس بالأمر المستحسن. delay: التأخير قبل بدء التنفيذ بالمليثانية (1000 مليثانية = ثانية واحدة). مبدئيًا يساوي 0. arg1, arg2…: وُسطاء الدالة (ليست مدعومة في IE9-) إليك هذه الشيفرة التي تستدعي sayHi() بعد ثانيةً واحدة: function sayHi() { alert('Hello'); } setTimeout(sayHi, 1000); مع المُعاملات: function sayHi(phrase, who) { alert( phrase + ', ' + who ); } setTimeout(sayHi, 1000, "Hello", "John"); // Hello, John لو كان المُعامل الأول سلسلة نصية فستصنع جافاسكربت دالة منها. أي أنّ هذا سيعمل: setTimeout("alert('Hello')", 1000); ولكن استعمال السلاسل النصية غير مستحسن. استعمل الدوال السهمية بدلًا عنها: setTimeout(() => alert('Hello'), 1000); مرّر الدالة لكن لا تشغّلها يُخطئ المبرمجون المبتدئون أحيانًا فيُضيفون أقواس () بعد الدالة: // هذا خطأ! setTimeout(sayHi(), 1000); لن يعمل ذلك إذ يتوقّع setTimeout إشارة إلى الدالة، بينما هنا sayHi() يشغّل الدالة وناتج التنفيذ هو الذي يُمرّر إلى setTimeout. في حالتنا ناتج sayHi() ليس معرّفًا undefined (إذ لا تُعيد الدالة شيئًا)، ويعني ذلك أنّ عملنا ذهب سدًى ولم نُجدول أي شيء. الإلغاء باستعمال clearTimeout نستلمُ حين نستدعي setTimeout «هويّةَ المؤقّت» timerId ويمكن استعمالها لإلغاء عملية التنفيذ. صياغة الإلغاء: let timerId = setTimeout(...); clearTimeout(timerId); في الشيفرة أسفله نُجدول الدالة ثمّ نُلغيها (غيّرنا الخطّة العبقرية)، بهذا لا يحدث شيء: let timerId = setTimeout(() => alert("never happens"), 1000); alert(timerId); // هويّة المؤقّت clearTimeout(timerId); alert(timerId); // ذات الهويّة (لا تصير null بعد الإلغاء) يمكن أن نرى من ناتج التابِع alert أنّ هويّة المؤقّت (في المتصفّحات) هي عدد. يمكن أن تكون في البيئات الأخرى أيّ شيء آخر. فمثلًا في Node.js نستلم كائن مؤقّت فيه توابِع أخرى. نُعيد بأن ليس هناك مواصفة عالمية متّفق عليها لهذه التوابِع، فما من مشكلة في هذا. يمكنك مراجعة مواصفة HTML5 للمؤقّتات (داخل المتصفّحات) في فصل المؤقّتات. setInterval صياغة التابِع setInterval هي ذات setTimeout: let timerId = setInterval(func|code, [delay], [arg1], [arg2], ...) ولكلّ المُعاملات ذات المعنى. ولكن على العكس من setTimeout فهذا التابِع يشغّل الدالة مرّة واحدة ثمّ أخرى وأخرى وأخرى تفصلها تلك الفترة المحدّدة. يمكن أن نستدعي clearInterval(timerId) لنُوقف الاستدعاءات اللاحقة. سيعرض المثال الآتي الرسالة كلّ ثانيتين اثنتين، وبعد خمس ثوان يتوقّف ناتجها: // نكرّر التنفيذ بفترة تساوي ثانيتين let timerId = setInterval(() => alert('tick'), 2000); // وبعد خمس ثوان نُوقف الجدولة setTimeout(() => { clearInterval(timerId); alert('stop'); }, 5000); الوقت لا يتوقّف حين تظهر مُنبثقة alert تُواصل عقارب ساعة المؤقّت الداخلي (في أغلب المتصفّحات بما فيها كروم وفَيَرفُكس) بالمضيّ حتّى حين عرض alert/confirm/prompt. لذا متى ما شغّلت الشيفرة أعلاه ولم تصرف نافذة alert بسرعة، فسترى نافذة alert الثانية بعد ذلك مباشرةً، بذلك تكون الفترة الفعلية بين التنبيهين أقلّ من ثانيتين. تداخل setTimeout لو أردنا تشغيل أمر كلّ فترة، فهناك طريقتين اثنتين. الأولى هي setInterval. والثانية هي setTimeout متداخلة هكذا: /** بدل كتابة: let timerId = setInterval(() => alert('tick'), 2000); */ let timerId = setTimeout(function tick() { alert('tick'); timerId = setTimeout(tick, 2000); // (*) }, 2000); تابِع setTimeout أعلاه يُجدول الاستدعاء التالي ليحدث بعد نهاية الأول (لاحظ (*)). كتابة توابِع setTimeout متداخلة يعطينا شيفرة مطواعة أكثر من setInterval. بهذه الطريقة يمكن تغيير جدولة الاستدعاء التالي حسب ناتج الحالي. فمثلًا علينا كتابة خدمة تُرسل طلب بيانات إلى الخادوم كلّ خمس ثوان، ولكن لو كان الخادوم مُثقلًا بالعمليات فيجب أن تزداد الفترة إلى 10 فَـ 20 فَـ 40 ثانية وهكذا… إليك فكرة عن الشيفرة: let delay = 5000; let timerId = setTimeout(function request() { ...نُرسل الطلب... if (لو فشل الطلب لوجود ضغط على الخادوم) { // نزيد الفترة حتّى الطلب التالي delay *= 2; } timerId = setTimeout(request, delay); }, delay); ولو كانت الدوال التي نُجدولها ثقيلة على المعالج فيمكن أن نقيس الزمن الذي أخذتها عملية التنفيذ الحالية ونؤجّل أو نقدّم الاستدعاء التالي. يتيح لنا تداخل التوابِع setTimeout بضبط الفترة بين عمليات التنفيذ بدقّة أعلى ممّا تقدّمه setInterval. لنرى الفرق بين الشيفرتين أسفله. الأولى تستعمل setInterval: let i = 1; setInterval(function() { func(i++); }, 100); الثانية تستعمل setTimeout متداخلة: let i = 1; setTimeout(function run() { func(i++); setTimeout(run, 100); }, 100); سيُشغّل المُجدول الداخلي func(i++) كلّ 100 مليثانية حسب setInterval: هل لاحظت ذلك؟ التأخير الفعلي بين استدعاءات func التي ينفّذها setInterval أقل مما هي عليه في الشيفرة! هذا طبيعي إذ أنّ الوقت الذي يأخذه تنفيذ func يستهلك بعضًا من تلك الفترة أيضًا. يمكن أيضًا بأن يصير تنفيذ func أكبر ممّا توقعناه على حين غرّة ويأخذ وقتًا أطول من 100 مليثانية. في هذه الحال ينتظر المحرّك انتهاء func ثمّ يرى المُجدول: لو انقضى الوقت يشغّل الدالة مباشرةً. دومًا ما تأخذ الدالة وقتًا أطول من delay مليثانية في هذه الحالات الهامشية، إذ تجري الاستدعاءات واحدةً بعد الأخرى دون هوادة. وإليك صورة setTimeout المتداخلة: تضمن setTimeout المتداخلة لنا التأخير الثابت (100 مليثانية في حالتنا). ذلك لأنّ الاستدعاء التالي لا يُجدول إلا بعد انتهاء السابق. كنس المهملات وردود نداء التابِعين setInterval و setTimeout تُنشأ إشارة داخلية إلى الدالة (وتُحفظ في المُجدول) متى مرّرتها إلى إلى setInterval/setTimeout، وهذا يمنع كنس الدالة على أنّها مهملات، حتّى لو لم تكن هناك إشارات إليها. // تبقى الدالة في الذاكرة حتّى يُستدعى `clearInterval`. setTimeout(function() {...}, 100); ولكن هناك تأثير جانبي لذلك كالعادة، فالدوال تُشير إلى بيئتها المُعجمية الخارجية. لذا طالما «تعيش»، تعيش معها المتغيرات الخارجية أيضًا، وهي أحيانًا كبيرة تأخذ ذاكرة أكبر من الدالة ذاتها. لذا، متى ما لم ترد تلك الدالة المُجدولة فالأفضل أن تُلغيها حتّى لو كانت صغيرة جدًا. جدولة setTimeout بتأخير صفر إليك الحالة الخاصة: setTimeout(func, 0) أو setTimeout(func). يُجدول هذا التابِع ليحدث تنفيذ func بأسرع ما يمكن، إلّا أن المُجدول لن يشغّلها إلا بعد انتهاء السكربت الذي يعمل حاليًا. أي أنّ الدالة تُجدول لأن تعمل «مباشرةً بعد» السكربت الحالي. فمثلًا تكتب هذه الشيفرة "Hello" ثم مباشرة "World": setTimeout(() => alert("World")); alert("Hello"); يعني السطر الأوّل «ضع الاستدعاء في التقويم بعد 0 مليثانية»، إلّا أنّ المُجدول لا «يفحص تقويمه» إلّا بعد انتهاء السكربت الحالي، بهذا تصير "Hello" أولًا وبعدها تأتي "World". كما أنّ هناك استعمالات متقدّمة خصّيصًا للمتصفّحات للمهلة بالتأخير صفر هذه، وسنشرحها في الفصل «حلقة الأحداث: المهام على المستويين الجُسيمي والذرّي». في الواقع، فالتأخير الصفر هذا ليس صفرًا (في المتصفّحات) تحدّ المتصفّحات من التأخير بين تشغيل المؤقّتات المتداخلة. تقول مواصفة HTML5: «بعد المؤقّتات المتداخلة الخمسة الأولى، تُجبر الفترة لتكون أربع مليثوان على الأقل.». لنرى ما يعني ذلك بهذا المثال أسفله. يُعيد استدعاء setTimeout جدولة نفسه بمدّة تأخير تساوي صفر، ويتذكّر كل استدعاء الوقت الفعلي بينه وبين آخر استدعاء في مصفوفة times. ولكن، ما هي التأخيرات الفعلية؟ لنرى بأعيننا: let start = Date.now(); let times = []; setTimeout(function run() { times.push(Date.now() - start); // نحفظ التأخير من آخر استدعاء if (start + 100 < Date.now()) alert(times); // نعرض التأخيرات بعد 100 مليثانية else setTimeout(run); // وإلّا نُعيد الجدولة }); // إليك مثالًا عن الناتج: // 1,1,1,1,9,15,20,24,30,35,40,45,50,55,59,64,70,75,80,85,90,95,100 تعمل المؤقّتات الأولى مباشرةً (كما تقول المواصفة)، وبعدها نرى 9, 15, 20, 24.... تلك الأربع مليثوان الإضافية هي التأخير المفروض بين الاستدعاءات. حتّى مع setInterval بدل setTimeout، ذات الأمر: تعمل الدالة setInterval(f) أوّل f مرّة بمدّة تأخير صفر، وبعدها تزيد أربع مليثوان لباقي الاستدعاءات. سبب وجود هذا الحدّ هو من العصور الحجرية (متعوّدة دايمًا) وتعتمد شيفرات كثيرة على هذا السلوك. بينما مع نسخة الخواديم من جافاسكربت فهذا الحدّ ليس له وجود، وهناك أيضًا طُرق أخرى لبدء المهام التزامنية مباشرةً، مثل setImmediate للغة Node.js، هذا قلنا بأنّ هذا يخصّ المتصفّحات فقط. ملخص يتيح لنا التابِعان setTimeout(func, delay, ...args) وsetInterval(func, delay, ...args) تشيل الدالة func مرّة أو كلّ فترة حسب كذا مليثانية (delay). لإلغاء التنفيذ علينا استدعاء clearTimeout/clearInterval بالقيمة التي أعاداها setTimeout/setInterval. يُعدّ استدعاء الدوال setTimeout تداخليًا خيارًا أفضل من setInterval إذ يُتيح لنا ضبط الوقت بين كلّ عملية استدعاء بدقّة. الجدولة بضبط التأخير على الصفر باستعمال setTimeout(func, 0) (كما واستعمال setTimeout(func)) يكون حين نريدها «بأقصى سرعة ممكنة، متى انتهى السكربت الحالي». يَحدّ المتصفّح من أدنى تأخير بعد استدعاء setTimeout أو setInterval المتداخل الخامس (أو أكثر) - يَحدّه إلى 4 مليثوان، وهذا لأسباب تاريخية لاحظ بأنّ توابِع الجدولة لا تضمن التأخير كما هو حرفيًا. فمثلًا يمكن أن تكون مؤقّتات المتصفّحات أبطأ لأسباب عديدة: المعالج مُثقل بالعمليات. لسان المتصفّح يعمل في الخلفية. يعمل الحاسوب المحمول على البطارية. يمكن لهذا كله رفع دقّة المؤقّت الدنيا (أي أدنى تأخير ممكن) لتصير 300 مليثانية أو حتى 1000 مليثانية حسب المتصفّح وإعدادات الأداء في نظام التشغيل. تمارين اكتب الناتج كل ثانية الأهمية: 5 اكتب الدالة printNumbers(from, to) لتكتب عددًا كلّ ثانية بدءًا بِـ from وانتهاءً بِـ to. اصنع نسختين من الحل. واحدةً باستعمال setInterval. واحدةً باستعمال setTimeout متداخلة. الحل باستعمال setInterval: function printNumbers(from, to) { let current = from; let timerId = setInterval(function() { alert(current); if (current == to) { clearInterval(timerId); } current++; }, 1000); } // الاستعمال: printNumbers(5, 10); باستعمال setTimeout متداخلة: function printNumbers(from, to) { let current = from; setTimeout(function go() { alert(current); if (current < to) { setTimeout(go, 1000); } current++; }, 1000); } // الاستعمال: printNumbers(5, 10); لاحظ كلا الحلّين: هناك تأخير أولي قبل أول عملية كتابة إذ تُستدعى الدالة بعد 1000ms في أوّل مرة. لو أردت تشغيل الدالة مباشرةً فعليك كتابة استدعاء إضافي في سطر آخر هكذا: function printNumbers(from, to) { let current = from; function go() { alert(current); if (current == to) { clearInterval(timerId); } current++; } go(); // هنا let timerId = setInterval(go, 1000); } printNumbers(5, 10); ماذا سيعرض setTimeout؟ الأهمية: 5 جدول أحدهم في الشيفرة أسفله استدعاء setTimeout، وثمّ كتب عملية حسابية ثقيلة لتعمل (وهي تأخذ أكثر من 100 مليثانية حتى تنتهي). متى ستعمل الدالة المُجدولة؟ بعد الحلقة؟ قبل الحلقة؟ في بداية الحلقة؟ ما ناتج alert؟ let i = 0; setTimeout(() => alert(i), 100); // ? // عُدّ بأنّ الوقت اللازم لتنفيذ هذه الدالة يفوق 100 مليثانية for(let j = 0; j < 100000000; j++) { i++; } الحل لن يُشغّل أيّ تابِع setTimeout إلا بعدما تنتهي الشيفرة الحالية. ستكون قيمة i هي القيمة الأخيرة: 100000000. let i = 0; setTimeout(() => alert(i), 100); // 100000000 // عُدّ بأنّ الوقت اللازم لتنفيذ هذه الدالة يفوق 100 مليثانية for(let j = 0; j < 100000000; j++) { i++; } ترجمة -وبتصرف- للفصل Scheduling: setTimeout and setInterval من كتاب The JavaScript language1 نقطة