چند قلاب ریاکت وجود دارند که در این نوشته به بررسی آنها میپردازیم. همچنین یک قلاب سفارشی میسازیم که تنها از قلاب useState برای اعتبار سنجی فرم استفاده میکند. اگر میخواهید مثال عملی آن را ببینید به این صفحه (+) مراجعه کنید. ایده کار این است که یک قلاب سفارشی ایجاد کنیم که برخی دادههای اولیه، اعتبارسنجی و اعتبارسنجی کنندهها را دریافت کند. کد نهایی این قلاب به صورت زیر خواهد بود. در ادامه طرز کار آن و همچنین شیوه استفاده از قلاب های React برای اعتبارسنجی یک فرم را بررسی میکنیم.
به طور کلی این قلاب یک آرایه با 2 بخش «حالت» (State) و 3 تابع بازگشت میدهد. در ادامه هر یک از این موارد را بررسی میکنیم.
متغیر فرم، حالت را به همراه تابع nChange و onClick برای همه بخشهای حالت نمایش میدهد و این بدان معنی است که دادههای اولیه فرم به صورت زیر در اختیار ما است:
متغیر فرم که از سوی قلاب بازگشت مییابد به صورت زیر خواهد بود:
بخش اعتبارسنجی پاسخهای اعتبارسنجی حاصل از valida-js را نگهداری میکند.
3 تابع دیگر به صورت زیر هستند:
ابتدا قلاب را از npm ایمپورت میکنیم و سپس آنها را درون تابع کامپوننت قرار میدهیم.
قلاب به نام useValidatedForm را میتوان دستکم در دو پارامتر مورد استفاده قرار داد که یکی دادههای اولیه برای فرم است (که باید طرحبندی کامل دادهها باشد) و پارامتر دوم آرایه قواعد اعتبارسنجی است. این قواعد اعتبارسنجی برای valida-js استفاده خواهد شد.
اینک مقدار بازگشتی از قلاب و شیوه مقداردهی اولیه آن را میدانیم. در ادامه فرم کوچکی را به وسیله آن مینویسیم:
بخشهای مهم به شرح زیر هستند:
در ابتدا بررسی میکنیم که در صورتی که مشخصهای خطا داشته باشد، چگونه باید آن را بخوانیم. شاید بهتر بود که یک تابع برای انجام این کار مینوشتیم، اما فعلاً از همین منطق ساده استفاده میکنیم. اگر آرایهای از خطاهای یک مشخصه، طولی بلندتر از 0 داشته باشید به این معنی است که خطایی رخ داده است و در غیر این صورت فرض ما این است که خطایی وجود ندارد.
بخش مهم دیگر به صورت زیر است:
جایی که شیء ورودی firstName را افراز میکنیم به این معنی است که props را به ورودی value، دستگیره onClick و دستگیره onChange میفرستیم. به این ترتیب قصد داریم متادیتای مشخصه را بهروزرسانی کنیم. همچنین مقدار ورودی را زمانی که کاربر در ورودی مینویسد بهروز کرده و اعتبارسنجی در مورد مشخصه خاص فرم را نیز بهروزرسانی میکنیم. از این رو validation.errors.firstName نیز در این حالت بهروزرسانی میشود.
یکی از موضوعات شگفتانگیز در این مورد آن است که اینک میتوانیم کامپوننتهای کنترل را بسازیم و validation.property، form.property و منطق دقیق را برای قرار دادن خطا و کلاسها در آنجا ارسال کنیم.
در این مثال تنها از یکی از قلابهای موجود روی React یعنی useState استفاده کردیم، اما شما میتوانید به بررسی قلابهای دیگر و روش استفاده از آنها نیز بپردازید.
منبع: فرادرس
حلقه Event یکی از مهمترین جنبههای جاوا اسکریپت است که باید درک شود. در این بخش از مقالات آموزش Node.js جزییات دقیق طرز کار جاوا اسکریپت با «نخ» (Thread) منفرد و شیوه مدیریت تابعهای ناهمگام را مورد بررسی قرار میدهیم. برای مطالعه بخش قبلی این سری مقالات آموزشی به لینک زیر مراجعه کنید:
بسیاری از افرادی که سالها تجربه برنامهنویسی جاوا اسکریپت دارند همچنان از طرز کار دقیق بسیاری از موارد در پشت پرده خبر ندارند. البته این که از جزییات دقیق این مفاهیم خبر نداشته باشید اشکالی ندارد، اما به طور معمول بهتر است با طرز کار کلی آنها آشنا باشید و ضمناً این احتمال وجود دارد که در این مرحله از آموزش Node.js در این خصوص کنجکاو شده باشید.
کد جاوا اسکریپت به صورت تک نخی اجرا میشود. بنابراین در هر زمان تنها یک اتفاق در حال وقوع است. این محدودیتی است که عملاً مفید است، چون سهولت زیادی در امر برنامهنویسی ایجاد میکند و نیازی به نگرانی در مورد مشکلات «همزمانی» (concurrency) وجود ندارد. شما باید صرفاً به روش نوشتن کد توجه داشته باشید و از هر چیزی که میتواند نخ را مسدود سازد مانند فراخوانیهای همگام شبکه یا حلقههای بینهایت جلوگیری کنید.
به طور کلی، در اغلب مرورگرها یک حلقه Event روی هر برگه مرورگر وجود دارد که موجب میشود هر پردازشی مجزا باشد و از مسدودسازی کل مرورگر در نتیجه یک پردازش سنگین یا حلقه بینهایت جلوگیری شود. این محیط چندین حلقه Event همزمان را مدیریت میکند تا برای نمونه فراخوانیهای API را مدیریت کند. بدین ترتیب Web Workers در حلقه رویداد خودشان اجرا میشوند. شما به صورت عمده باید در مورد این نگران باشید که کد روی یک حلقه رویداد منفرد اجرا خواهد شد و در هنگام نوشتن کد این ذهنیت جلوگیری از مسدودسازی را مداوماً داشته باشید.
هر کد جاوا اسکریپتی که بازگرداندن کنترل به حلقه event را بیش از حد طول بدهد، موجب مسدود شدن اجرای کد جاوا اسکریپت در صفحه خواهد شد و حتی ممکن است نخ UI را مسدود کند. در این حالت کاربر دیگر نمیتواند روی چیزی کلیک کند و یا کارهایی مانند اسکرول و نظایر آن انجام دهد.
تقریباً همه کارهای ابتدایی I/O در جاوا اسکریپت غیر مسدودکننده هستند. بنابراین درخواستهای شبکه، عملیات فایل سیستم Node.js و مواردی از این دست همگی غیر مسدودکننده هستند. مسدودکننده بودن یک استثنا است و به همین دلیل جاوا اسکریپت به طور عمده بر مبنای callback-ها کار میکند. البته در نسخههای اخیر تمرکز بیشتر روی promises و async/await انتقال یافته است.
پشته فراخوانی یک صف LIFO به معنی «ورودی آخر، خروجی اول» (Last In ،First Out) است. حلقه Event به طور پیوسته پشته فراخوانی را بررسی میکند تا ببیند آیا هیچ تابعی نیاز به اجرا دارد یا نه. در زمانی که چنین نیازی باشد، هر فراخوانی تابعی را که مییابد به پشته فراخوانی اضافه میکند تا به ترتیب اجرا شوند. همه شما «رد پشته خطا» (Error Stack Trace) را میشناسید و آن را در دیباگر یا در کنسول مرورگر دیدهاید.
مرورگر نامهای تابعها را در پشته فراخوانی بررسی میکند تا مطلع شود که کدام تابع فراوانی جاری را آغاز کرده است:

به مثال زیر توجه کنید:
این کد مقدار زیر را نمایش میدهد:
Foobarbaz
که مطابق انتظار است. زمانی که این کد اجرا میشود ابتدا ()foo فراخوانی میشود. درون ()foo ابتدا ()bar را فراخوانی میکنیم و سپس ()baz فراخوانی میشود. در این مرحله پشته فراخوانی مانند زیر است:

حلقه Event در هر تکرار بررسی میکند که آیا چیزی در پشته فراخوانی وجود دارد یا نه و آن را اجرا میکند:

تا این که پشته فراخوانی خالی شود.
مثال فوق معمولی به نظر میرسد و نکته خاصی ندارد: جاوا اسکریپت مواردی که باید اجرا شوند را مییابد و آنها را به ترتیب اجرا میکند. در ادامه با روش به تعویض انداختن (defer) یک تابع تا زمان خالی شدن پشته آشنا میشویم. کاربرد دستور زیر برای فراخوانی یک تابع است:
اما هر بار که تابع دیگری در کد اجرا شود، این دستور نیز اجرا خواهد شد. مثال زیر را در نظر بگیرید:
شاید شگفتزده شوید که کد فوق عبارت زیر را در خروجی نمایش میدهد:
Foobazbar
زمانی که این کد اجرا شود، ابتدا ()foo فراخوانی میشود. درون ()foo ابتدا setTimeout فراخوانی میشود و bar به عنوان یک آرگومان ارسال میشود. ما آن را طوری تنظیم میکنیم تا حد امکان به سرعت اجرا شود و مقدار 0 به عنوان تایمر ارسال میشود سپس ()baz را فراخوانی میکنیم. در این نقطه پشته فراخوانی مانند زیر خواهد بود:

در ادامه ترتیب اجرای همه تابعها را در برنامه مشاهده میکنید:

چرا چنین اتفاقی رخ میدهد؟ در بخش بعدی به این سؤال پاسخ میدهیم.
زمانی که ()setTimeout فراخوانی میشود، مرورگر یا Node.js تایمر را آغاز میکند. زمانی که تایمر منقضی شود، در این حالت از آنجا که مقدار 0 به عنوان timeout تعیین شده است، تابع callback در «صف پیام» (Message Queue) قرار میگیرد.
صف پیام جایی است که رویدادهای آغاز شده از سمت کاربر مانند رویدادهای کلیک و ضربههای کیبورد و یا واکشی پاسخها از صف پیش از آن که کد فرصت واکنش به آنها را داشته باشد صفبندی میشوند. رویدادهای DOM مانند onLoad نیز چنین خصوصیتی دارند.
این حلقه به پشته فراخوانی اولویت میدهد. این حلقه ابتدا همه چیز که در پشته فراخوانی بیاید پردازش میکند و زمانی که چیزی باقی نماند، اقدام به انتخاب موارد موجود در صف پیام میکند.
بدین ترتیب لازم نیست برای تابعهایی مانند setTimeout منتظر بمانیم یا این که صبر کنیم واکشی یا دیگر امور به پایان برسند، زیرا از سوی مرورگر ارائه شدهاند و روی نخهای خود زنده هستند. برای نمونه اگر مقدار timeout را با استفاده از دستور setTimeout روی 2 ثانیه تنظیم کرده باشید، لازم نیست 2 ثانیه منتظر بمانید چون انتظار در هر جایی رخ میدهد.
استاندارد ECMAScript 2015 مفهوم «صف کار» (Job Queue) را معرفی کرده است که از سوی Pomise-ها مورد استفاده قرار میگیرد و روشی برای اجرای نتیجه یک تابع async به محض امکان است و دیگر آن را در انتهای پشته فراخوانی قرار نمیدهیم. بدین ترتیب Promise-هایی که پیش از اتمام تابع جاری خاتمه یابند، درست پس از تابع جاری اجرا خواهند شد.
برای مقایسه میتوانید یک قطار هوایی شهر بازی را در نظر بگیرید. صف پیام شما را در پشت همه افرادی که قبل از شما در صف جای گرفتهاند قرار میدهد، در حالی که صف کار یک بلیت سریعالسیر است که اجازه میدهد درست پس از اتمام یک دور، مجدداً بیدرنگ سوار قطار هوایی شوید.
مثال:
کد فوق عبارت زیر را نمایش میدهد:
foobazshould be right after foo، before barbar
این تفاوت بزرگی است که بین Promise-ها (و البته async/await که بر مبنای Promise ساخته شده) با تابعهای ساده قدیمی ناهمگام که از طریق ()setTimeout یا دیگر API-های پلتفرم اجرا میشدند وجود دارد.
زمانی که تلاش میکنید حلقه رویداد Node.js را درک کنید، یک بخش مهم آن ()process.nextTick است. این بخش با حلقه رویداد به روشی خاص تعامل پیدا میکند. هر بار که حلقه رویداد یک دور کامل میزند آن را یک tick مینامیم.
زمانی که یک تابع را به ()process.nextTick ارسال میکنیم به موتور مرورگر دستور میدهیم که تابع را در انتهای عملیات جاری و پیش از آغاز تیک بعدی حلقه رویداد احضار کند:
حلقه رویداد مشغول پردازش کردن کد تابع جاری است. زمانی که این عملیات پایان گیرد، موتور جاوا اسکریپت همه تابعهای ارسالی در فراخوانیهای nextTick که در طی اجرای این عملیات ارسال شدهاند را اجرا میکند. به این ترتیب به موتور جاوا اسکریپت اعلام میکنیم که یک تابع را به روشی ناهمگام (پس از اجرای تابع جاری) اما به سرعت و بدون صفبندی پردازش کند. فراخوانی کردن (setTimeout(() => {}، 0 موجب میشود که تابع در تیک بعدی و بسیار بعدتر از زمانی که از ()nextTick استفاده میکنیم اجرا شود. از ()nextTick زمانی استفاده کنید که میخواهید مطمئن شوید در تکرار بعدی حلقه رویداد، کد حتماً اجرا خواهد شد.
هنگامی که میخواهیم بخشی از کد را به صورت ناهمگام اجرا کنیم، اما این کار در سریعترین زمان ممکن صورت گیرد، یک گزینه استفاده از تابع ()setImmediate است که از سوی Node.js ارائه شده است:
هر تابعی که به صورت آرگومان ()setImmediate ارسال شود یک callback است که در تکرار بعدی حلقه رویداد اجرا خواهد شد. اینک شاید از خود بپرسید ()setImmediate چه تفاوتی با (setTimeout(() => {}، 0 و یا ()process.nextTick دارد؟ تابعی که به ()process.nextTick ارسال شود در تکرار بعدی حلقه رویداد و پس از پایان یافتن عملیات اجرا خواهد شد. این بدان معنی است که همواره پیش از ()setTimeout و ()setImmediate اجرا میشود. یک callback به نام ()setTimeout با تأخیر 0 میلیثانیه بسیار به ()setImmediate شباهت دارد. ترتیب اجرا به عوامل مختلفی وابسته خواهد بود، اما هر دوی آنها در تکرار بعدی حلقه رویداد اجرا خواهند شد.
زمانی که کد جاوا اسکریپت مینویسیم ممکن است بخواهیم اجرای یک تابع را به تأخیر بیندازیم. به این منظور میتوان از ()setTimeout و ()setInterval برای زمانبندی اجرای تابع در آینده استفاده کرد.
()setTimeout
هنگامی که کد جاوا اسکریپت مینویسیم، میتوانیم با استفاده از دستور ()setTimeout اجرای یک تابع را به تأخیر بیندازیم. میتوان یک تابع callback تعیین کرد که بعدتر اجرا شود و مقداری برای میزان این تأخیر در اجرا بر مبنای میلیثانیه تعیین کرد:
این ساختار یک تابع جدید تعریف میکند. شما میتوانید تابع دیگر را درون آن فراخوانی کنید یا این که نام یک تابع موجود را به آن ارسال و پارامترهای آن را تعیین کنید:
()setTimeout یک شناسه تایمر بازگشت میدهد. این شناسه عموماً استفادهای ندارد، اما میتوانید آن را ذخیره کنید و در صورتی که بخواهید اجرای این تابع زمانبندیشده را حذف کنید آن را پاک کنید:
اگر میزان تأخیر را برابر با 0 تعیین کنید، تابع callback در اولین فرصت ممکن، اما پس از اجرای تابع جاری اجرا خواهد شد:
کد فوق عبارت زیر را نمایش میدهد:
before after
این حالت به طور خاص در مواردی که میخواهیم از مسدود شدن CPI روی وظایف سنگین جلوگیری کنیم و اجازه دهیم تابعهای دیگر نیز در زمان اجرای یک محاسبه سنگین اجرا شوند مفید خواهد بود. این کار از طریق صفبندی تابعها در یک جدول زمانبندی ممکن خواهد بود. برخی مرورگرها (مانند IE و Edge) یک متد ()setImmediate را پیادهسازی کردهاند که دقیقاً همین کارکرد را انجام میدهد، اما استاندارد نیست و روی مرورگرهای دیگر وجود ندارد. اما این تابع که معرفی کردیم در Node.js یک استاندارد محسوب میشود.
()setInterval یک تابع مشابه ()setTimeout است و تنها یک تفاوت دارد. به جای اجرا کردن یکباره تابع callback این تابع آن را برای همیشه و در بازههای زمانی معین شده (بر حسب میلیثانیه) اجرا میکند:
تابع فوق هر 2 ثانیه یک بار اجرا میشود، مگر اینکه با استفاده از clearInterval و ارسال شناسه بازهای که در پی اجرای setInterval بازگشت مییابد از آن بخواهیم متوقف شود:
فراخوانی clearInterval درون تابع callback setInterval رویهای رایج است و بدین ترتیب میتوان در مورد اجرای مجدد یا توقف آن یک تصمیمگیری خودکار داشت. برای نمونه کد زیر کار دیگری را اجرا میکند، مگر اینکه App.somethingIWait دارای مقدار arrived باشد:
setTimeout یک تابع را هر n میلیثانیه یک بار اجرا میکند و هیچ ملاحظهای در مورد زمان پایان اجرای تابع ندارد. اگر یک تابع همواره زمان مشابهی را نیاز داشته باشد، این روش مناسب خواهد بود:

اما ممکن است تابع برای نمونه بسته به شرایط شبکه، برای اجرا به زمان متفاوتی نیاز داشته باشید:

و حتی ممکن است این زمان طولانی اجرا با تابع بعدی همپوشانی پیدا کند:

برای جلوگیری از این وضعیت میتوان یک setTimeout بازگشتی زمانبندی کرد تا زمانی که تابع callback به پایان میرسد فراخوانی شود:
برای دستیابی به این سناریو:

setTimeout و setInterval هر دو در Node.js از طریق ماژول Timers در اختیار ما قرار گرفتهاند. ()Node.js تابع setImmediate را نیز ارائه کرده است که معادل استفاده از دستور زیر است:
از این دستور به طور غالب برای کار با حلقه Event در Node.js استفاده میشود. بدین ترتیب به پایان بخش ششم این سری مقالههای آموزش Node.js میرسیم. در بخش بعدی در مورد برنامهنویسی ناهمگام و Callback-ها توضیح خواهیم داد. برای مطالعه بخش بعدی به لینک زیر مراجعه کنید:
در این مطلب، الگوریتم و کد جمع کردن چند جمله ای ها همراه با ارائه مثالی بیان شده است؛ همچنین، کد جمع کردن دو چند جملهای در زبانهای برنامهنویسی «سیپلاسپلاس» (++C)، «جاوا» (Java)، «پایتون 3» (Python3)، «سیشارپ» (#C) , «پیاچپی» (PHP)ارائه شده است.
دو چند جملهای به صورت دو آرایه زیر تعریف شدهاند. هدف نوشتن تابعی است که بتواند دو چندجملهای داده شده را با یکدیگر جمع کند.
ورودیها:
A[] = {5, 0, 10, 6}
B[] = {1, 2, 4}
خروجی:
sum[] = {6, 2, 14, 6}
اولین آرایه ورودی، نشاگر «5 + 0x^1 + 10x^2 + 6x^3» و دومین آرایه، نشانگر «1 + 2x^1 + 4x^2» است. آرایه خروجی نیز نشانگر چندجملهای «6 + 2x^1 + 14x^2 + 6x^3» است.
جمع کردن دو چند جملهای، از ضرب کردن آنها سادهتر است. مقداردهی اولیه با یکی از دو چندجملهای انجام میشود، سپس، چند جملهای دیگر خوانده و همه عبارات آن به نتیجه، اضافه میشوند. الگوریتم مورد استفاده برای جمع کردن دو چند جملهای، به صورت زیر است.
افزودن ([A[0..m-1], B[0..n01)
در ادامه، پیادهسازی الگوریتم جمع کردن دو چند جملهای، در زبانهای برنامهنویسی گوناگون انجام شده است.
First polynomial is 5 + 0x^1 + 10x^2 + 6x^3 Second polynomial is 1 + 2x^1 + 4x^2 Sum polynomial is 6 + 2x^1 + 14x^2 + 6x^3
پیچیدگی زمانی الگوریتم و برنامه بالا، برابر با (O(m+n است که در آن، m و n به ترتیب مرتبه چند جملهایهای داده شده هستند.
در این مقاله برخی مفاهیم مهم مرتبط با برنامه نویسی ناهمگام و کارکرد آنها در مرورگرهای وب و جاوا اسکریپت را بررسی میکنیم. پس از مطالعه مقاله حاضر، این مفاهیم را درک خواهید کرد و آماده مطالعه بخشهای بعدی این سری مقالات آموزشی جاوا اسکریپت خواهید بود. همچنین برای مطالعه قسمت قبلی این مجموعه مطلب آموزشی میتوانید به مطلب «تمرین ساخت شیئ در جاوا اسکریپت (بخش دوم) — راهنمای کاربردی» مراجعه کنید.
به طور معمول کد یک برنامه به صورت خطی و مستقیم اجرا میشود و هر لحظه تنها یک کار در حال انجام است. اگر تابعی برای اجرا به نتیجه تابع دیگری نیاز داشته باشد، باید منتظر بماند تا تابع دیگر کار خود را تمام کرده و نتیجه را بازگشت دهد و تا زمانی که این اتفاق بیفتد، کل برنامه عملاً از نظر کاربر متوقف میشود.
برای نمونه کاربران Mac برخی اوقات این وضعیت را به شکل یک کرسر رنگینکمانی در حال چرخش تجربه میکنند. سیستم عامل با نمایش این کرسر در واقع اعلام میکند که برنامه کنونی که استفاده میکنید باید متوقف مانده و منتظر باشد تا چیز دیگری انجام یابد و این کار به مدتزمانی نیاز دارد و بدینوسیله اعلام میکنیم که جای نگرانی نیست و مشغول کار هستیم.

این تجربه ناخوشایندی است و استفاده مناسبی از توان پردازشی رایانه به خصوص در این عصر که رایانهها چندین هسته پردازشی دارند، به حساب نمیآید. این که منتظر بنشینیم تا کار دیگری پایان یابد، در حالی که میتوان آن را به هسته پردازشی دیگری واگذار کرد تا در زمان خاتمه کار به ما اطلاع دهد. بدین ترتیب میتوان کارها را به صورت همزمان پیش برد که اساس «برنامهنویسی ناهمگام» (asynchronous programming) را تشکیل میدهد. همه چیز به محیط برنامه که استفاده میکنید بستگی دارد تا با ارائه API–های مختلف امکان اجرای نامتقارن وظایف را فراهم سازد. در زمینه برنامهنویسی وب این محیط مرورگر وب است.
تکنیکهای ناهمگام در برنامهنویسی و به خصوص برنامهنویسی وب بسیار مهم هستند. زمانی که یک وب اپلیکیشن در مرورگر اجرا میشود و یک دسته کد سنگین را بدون بازگرداندن کنترل به مرورگر اجرا میکند، ممکن است این گونه به نظر برسد که مرورگر قفل شده است. این وضعیت «مسدودسازی» (blocking) نامیده میشود. در این حالت مرورگر نمیتواند ورودی کاربر را مدیریت و وظایف دیگر را اجرا کند و تا زمانی که کنترل از پردازنده به مرورگر بازنگردد تداوم خواهد داشت.
در ادامه به بررسی چند مثال میپردازیم تا معنی دقیق مسدودسازی را درک کنیم.
در این مثال (+) چنان که ملاحظه میکنید یک «شنونده رویداد» به دکمه اضافه شده است تا زمانی که دکمه کلیک میشود یک عملیات زمانگیر را اجرا کند. در این عملیات 10 میلیون تاریخ محاسبه میشود و نتیجه در کنسول نمایش پیدا میکند. سپس یک پاراگراف به DOM اضافه خواهد شد.
زمانی که این مثال را اجرا میکنید، کنسول جاوا اسکریپت را باز کنید و سپس روی دکمه کلیک کنید، متوجه خواهید شد که پاراگراف مزبور به جز تا زمانی که تاریخها محاسبه نشده باشند و پیام نهایی در کنسول نمایش نیافته باشد در صفحه ظاهر نمیشود. این کد به همان ترتیب که نوشته شده اجرا میشود و عملیات بعدی تا زمانی که عملیات قبلی به پایان نرسیده باشد اجرا نخواهد شد.
نکته: مثال قبلی بسیار غیر واقعگرایانه است. ما هرگز در یک وب اپلیکیشن یک میلیون تاریخ را محاسبه نمیکنیم. این مثال صرفاً جهت این ارائه شده است که ایده اولیهای از موضوع داشته باشید.
در مثال دوم (+) چیزی را شبیهسازی میکنیم که اندکی واقعگرایانهتر است و احتمال بیشتری برای دیدن آن در یک صفحه وب وجود دارد. ما تعاملپذیری کاربر را از طریق رندر کردن UI مسدود میکنیم. در این مثال دو دکمه داریم:
اگر دکمه اول را کلیک کنید و سپس به سرعت روی دکمه دوم کلیک نمایید، متوجه خواهید شد که هشدار تا زمانی که رندر شدن دایرهها به پایان نرسیده است، ظاهر نمیشود. عملیات نخست، عملیات دوم را مسدود میکند تا این که اجرایش به پایان برسد.
نکته: در این مورد کد زشت است و قصد ما صرفاً ایجاد حالت مسدودسازی بوده است، اما این مشکل رایجی است که توسعهدهندگان وباپلیکیشنهای واقعی همواره با آن دستبهگریبان هستند.
دلیل این وضعیت آن است که جاوا اسکریپت به بیان کلی یک زبان برنامهنویسی «تک نخی» (single threaded) است. در این مرحله ابتدا باید با مفهوم «نخ» (thread) در برنامهنویسی آشنا شویم.
نخ اساساً یک پردازش منفرد است که برنامه میتواند برای اجرای وظیفهای به خدمت بگیرد. هر نخ میتواند در هر زمان تنها یک وظیفه منفرد را اجرا کند:
Task A --> Task B --> Task C
هر وظیفه به صورت ترتیبی اجرا میشود و برای این که وظیفهای بتواند شروع شود باید وظیفه قبلی به پایان رسیده باشد.
چنان که پیشتر گفتیم، بسیاری از رایانهها در حال حاضر دارای پردازندههای چندهستهای هستند و از این رو میتوانند چندین کار را به طور همزمان اجرا کنند. زبانهای برنامهنویسی که میتوانند از چندین نخ پشتیبانی کنند، میتوانند برای اجرای وظایف به صورت همزمان چند هسته پردازشی را به کار بگیرند.
Thread 1: Task A --> Task B Thread 2: Task C --> Task D
جاوا اسکریپت به صورت سنتی یک زبان تک نخی است. حتی اگر چندین هسته پردازشی را به کار بگیرید، در هر حال باید وظایف را روی یک نخ منفرد اجرا کنید که «نخ اصلی» (main thread) نام دارد. بدین ترتیب مثال فوق به صورت زیر اجرا میشود:
Main thread: Render circles to canvas --> Display alert()
البته پس از مدتی جاوا اسکریپت از برخی ابزارها کمک گرفت تا چنین مشکلاتی را رفع کند. «وب ورکر» (Web worker) امکان ارسال چند پردازش جاوا اسکریپت به یک نخ جدا را که worker نام دارد فراهم میسازد. بدین ترتیب میتوان چندین دسته کد جاوا اسکریپت را به صورت همزمان اجرا کرد. به طور کلی از یک worker برای اجرای پردازشهای سنگین و برداشتن این وظیفه از دوش نخ اصلی استفاده میشود. بدین ترتیب تعامل کاربر با مرورگر مسدود نمیشود.
Main thread: Task A --> Task C Worker thread: Expensive task B
با در نظر داشتن این موضوع، نگاهی به این مثال (+) میاندازیم. کنسول مرورگر را باز نگهدارید. در واقع این مثال یک بازنویسی از مثال قبلی است که 10 میلیون تاریخ را در نخ worker جداگانهای اجرا میکرد. اکنون زمانی که روی دکمه کلیک کنید، مرورگر میتواند پاراگراف را پیش از اتمام محاسبه تاریخها نمایش دهد. به این ترتیب عملیات نخست موجب مسدود شدن عملیات دوم نمیشود.
وب ورکرها کاملاً مفید هستند، اما آنها نیز محدودیتهای خاص خود را دارند. مهمترین محدودیت وب ورکرها این است که نمیتوانند به DOM دسترسی داشته باشند. بنابراین نمیتوان انتظار داشت که یک وب ورکر مستقیماً کاری برای بهروزرسانی UI انجام دهد. ما نمیتوانیم 1 میلیون دایره آبی را درون worker رندر کنیم، چون وظیفه آن صرفاً محاسبات است.
دومین مشکل این است که گرچه کد در یک ورکر مسدودسازی ندارد، اما همچنان در ذات خود همگام است. این وضعیت در مواردی که تابعی به نتایج پردازشهای قبلی نیاز داشته باشد، مشکلساز خواهد بود. نمودارهای نخ زیر را در نظر بگیرید:
Main thread: Task A --> Task B
در این حالت، وظیفه A کاری مانند واکشی کردن یک تصویر از سرور را انجام میدهد و وظیفه B کاری مانند اعمال یک فیلتر را روی تصویر اجرا میکند. اگر ابتدا شروع به اجرای وظیفه A و سپس بیدرنگ شروع به اجرای وظیفه B بکنید، با خطایی مواجه خواهید شد، چون تصویر هنوز آماده نشده است.
Main thread: Task A --> Task B --> |Task D| Worker thread: Task C -----------> | |
در این حالت، فرض کنید وظیفه D از نتایج هر دو وظیفه B و C استفاده میکند. اگر بتوانیم تضمین کنیم که این نتایج هر دو به صورت همزمان آماده خواهند بود، در این صورت مشکلی وجود نخواهد داشت، اما چنین وضعیتی نامحتمل است. اگر وظیفه D تلاش کند زمانی که ورودیاش هنوز آماده نشده است، اجرا شود با خطا مواجه خواهد شد.
مرورگرها برای اصلاح چنین خطاهایی به ما اجازه میدهند که برخی عملیات را به صورت ناهمگام اجرا کنیم. قابلیتهایی مانند Promise-ها امکان تعیین حالت اجرایی برای یک عملیات (برای نمونه واکشی یک تصویر از سرور) و سپس صبر کردن برای بازگشت نتیجه تا پیش از اجرای عملیات بعدی را فراهم میسازند:
Main thread: Task A Task B Promise: |__async operation__|
از آنجا که عملیات در جای دیگری اتفاق میافتد، در زمانی که عملیات ناهمگام در حال پردازش است، نخ اصلی مسدود نمیشود. در بخش بعدی این سری مقالات آموزشی به بررسی روشهای نوشتن کدهای ناهمگام خواهیم پرداخت.
اصول طراحی مدرن نرمافزار به طور فزایندهای پیرامون استفاده از برنامهنویسی ناهمگام توسعه مییابند تا به برنامهها امکان اجرای همزمان چندین کار را بدهند. زمانی که از API-های مدرن و قدرتمندتر استفاده میکنید، مواردی بیشتری را خواهید یافت که تنها راه اجرای یک وظیفه برنامهنویسی ناهمگام است. نوشتن کد ناهمگام دشوارتر است و عادت کردن به آن به کمی زمان نیاز دارد، اما به مرور آسانتر خواهد شد. در بخشهای بعدی این سری مقالات آموزشی به بررسی بیشتر دلیل اهمیت کدهای ناهمگام و روشهای طراحی کد که از بروز مشکلات مطرح شده در این مقاله جلوگیری میکنند خواهیم پرداخت. برای مطالعه بخش بعدی به لینک زیر رجوع کنید:
منبع: فرادرس
زمانی که بخواهیم اطلاعاتی در مورد کاربران یک وبسایت ذخیره کنیم، سیستم ورود و ثبت نام کاربر بسیار مفید خواهد بود. این موضوع در مورد همه وبسایتها از وبسایتهای آموزشی که میتوانند روند پیشرفت کاربر در طی دوره آموزشی را ذخیره کنند و به آن نمره بدهند تا وبسایتهای فروشگاهی که اطلاعاتی در مورد خریدهای گذشته مشتری ذخیره میکنند کاربرد دارد. در این راهنما، روش ایجاد فرم ورود و ثبت نام کاربر را از صفر تا صد با هم مرور خواهیم کرد.
نخستین گام، ساخت خود فرم ورود و ثبت نام است. این فرمها در واقع بسیار ساده هستند. فرم ثبت نام تنها نام کاربری، ایمیل و رمز عبور کاربر را میپرسد. نام کاربری و ایمیل برای همه افرادی که میخواهند ثبت نام کنند باید یکتا باشند. اگر هر کس تلاش کند با استفاده از آدرس ایمیل مشابهی دو حساب باز کند، یک پیام خطا به وی نمایش داده میشود که این ایمیل قبلاً استفاده شده است.
در ادامه کد HTML فرم ثبت نام را میبینید. شما باید آن را در فایلی به نام register.php ذخیره کنید:
این فرم کاملاً ابتدایی است، اما در آن از HTML5 استفاده کردهایم تا برخی اعتبارسنجیهای بسیار مقدماتی ورودی را اجرا کنیم. برای نمونه استفاده از “type=”email به کاربران هشدار میدهد که آدرس ایمیلی که وارد کردهاند دارای قالببندی صحیح نیست. به طور مشابه، استفاده از خصوصیت pattern روی نام کاربری موجب میشود مطمئن شویم در نام کاربری تنها کاراکترهای حرف و رقم وجود دارند.
در ادامه کد HTML فرم ورود را مشاهده میکنید. این کدها را در فایلی به نام login.php قرار دهید:
در ادامه برخی کدهای CSS را میبینید که روی این فرمها اعمال میشوند:
این فایل شامل برخی قواعد استایلدهی برای نمایش پیامهای خطا و عناوین است. کدهای HTML و CSS که در این بخش ارائه شدند میتوانند به عنوان مبنایی برای پروژه شما جهت ایجاد فرمهای اختصاصی استفاده شوند و ممکن است استایلدهی و فیلدهای ورودی متفاوتی داشته باشند.

گام بعدی ایجاد یک جدول کاربر است که همه اطلاعات را در مورد کاربران ثبت نام کرده ذخیره میکند. در این مورد جدول صرفاً شامل چهار ستون به این صورت خواهد بود: یک ستون برای شناسه با افزایش خودکار، یک نام کاربری یکتا، یک ایمیل و یک رمز عبور
میتوان از گزارههای SQL زیر برای ایجاد سریع جدول استفاده کرد:
سپس یک فایل به نام config.php ایجاد کرده و کدهای زیر را در آن مینویسیم تا به پایگاه داده وصل شویم:
نام پایگاه داده را در کد فوق به نام پایگاه دادهای که استفاده میکنید تغییر دهید. از این فایل برای برقراری یک اتصال به پایگاه داده استفاده میشود.
در نهایت زمان آن فرا میرسد که کارکرد ثبت نام را بنویسیم. تابع اصلی این کد به بررسی سوابق ثبت نام قبلی ایمیل ارائه شده میپردازد. اگر چنین نباشد، نام کاربری، ایمیل و رمز عبور را در پایگاه داده وارد میکنیم.
کد زیر را در فایل registration.php قرار دهید:
گام نخست این است که فایل config.php را بگنجانیم و یک «نشست» (Session) را آغاز کنیم. این فرایند به ما کمک میکند هر اطلاعاتی را که میخواهیم روی صفحههای مختلف به آن دسترسی داشته باشیم ذخیره کنیم.
سپس با وارسی تعیین شدن متغیر [‘POST[‘register_$، بررسی میکنیم که آیا کاربر روی دکمه Register کلیک کرده و فرم را تحویل داده است یا نه. همواره به یاد داشته باشید که ذخیرهسازی رمزهای عبور به صورت متن ساده ایده بدی است. به همین دلیل از تابع ()password_hash استفاده میکنیم. سپس هش محاسبه شده را در پایگاه داده خود ذخیره میکنیم. این تابع خاص یک هش 60 کاراکتری را با استفاده از salt محاسبه شده به صورت تصادفی ایجاد میکند.
در نهایت کوئری را اجرا میکنیم و بررسی میکنیم آیا هیچ ردیف غیر صفر برای آن آدرس ایمیل وجود دارد یا نه. اگر وجود داشته باشد پیامی به کاربر نمایش خواهیم داد که این آدرس ایمیل قبلاً در این وبسایت ثبت نام کرده است.
اگر هیچ ردیفی با مشخصه آدرس ایمیل ارائه شده موجود نباشد، اطلاعات عرضه شده را در پایگاه داده وارد میکنیم و به کاربر اطلاع میدهیم که ثبت نام موفق بوده است.
در آخرین گام از مراحل طراحی فرمهای ورود و ثبت نام، کدی را مینویسیم که باعث میشود کاربران بتوانند در سایت لاگین کنند. این بار کافی است اطلاعات موجود در پایگاه داده را بررسی کنیم تا ببینیم آیا ترکیب نام کاربری و رمز عبور وارد شده در فرم صحیح است یا نه.
در ادامه کد این بخش را ملاحظه میکنید که باید در فایلی به نام login.php قرار گیرد.
یک نکته مهم که باید توجه داشته باشید این است که ما نامهای کاربری و رمز عبور را در همزمان مقایسه نمیکنیم، چون رمز عبور عملاً به شکل هش شده ذخیره شده است. ابتدا باید هش را به کمک نام کاربری ارائه شده واکشی کنیم. زمانی که هش را به دست آوردیم، میتوانیم با استفاده از تابع اقدام به مقایسه رمز عبور و هش بکنیم.
زمانی که رمز عبور با موفقیت تأیید شود، متغیر [‘SESSION[‘user_id_$ را برابر با ID آن کاربر در پایگاه داده تعیین میکنیم. همچنین میتوانید مقدار متغیرهای دیگر را نیز بسته به نیاز تعیین کنید.
اغلب وبسایتها که از کاربر تقاضای ثبت نام میکنند، صفحههای دیگری نیز دارند که کاربران در آن اقدام به دسترسی و ذخیرهسازی دادههای خصوصی خود میکنند. میتوان از متغیرهای نشست برای حفاظت این صفحهها استفاده کرد. اگر متغیر نشست تعیین نشده باشد، میتوانید کاربر را به صفحه ورود هدایت کنید و در غیر این صورت کاربر میتواند به محتوای صفحات خود دسترسی داشته باشد.
تنها کاری که در این مرحله باید انجام دهید، این است که مطمئن شوید اسکریپت شامل ()session_start در ابتدای خود است.
در این راهنما آموختیم که یک سیستم ثبت نام و ورود کاربر را با استفاده از PHP بسازیم. زمانی که با مبانی سیستمهای ورود و ثبت نام آشنا شوید، میتوانید منطقهای پیچیدهتری را نیز پیادهسازی کنید و به کاربر امکان ریست کردن رمز عبور، تأیید کردن آدرس ایمیل و کارهایی از این دست را بدهید. همچنین میتوانید اعتبارسنجی فرانتاند بیشتری را با استفاده از خصوصیتهای HTML5 و JQuery اجرا کنید تا فرم کاربرپسندتری داشته باشید.
منبع: فرادرس