Table of Contents

جاوا اسکریپت ناهمزمان (Asynchronous JavaScript)

در این فصل با برنامه‌نویسی ناهمزمان (Asynchronous) در جاوا اسکریپت آشنا می‌شویم — از Event Handlers و Promises تا Web Workers. یاد می‌گیریم چگونه کدهای کارآمد و بدون مسدودی بنویسیم.

🎯 آنچه در این فصل یاد می‌گیرید:

  • Async vs Sync — تفاوت برنامه‌نویسی ناهمزمان و همزمان
  • Event Handlers — مدیریت رویدادها در جاوا اسکریپت
  • Promises — ایجاد و استفاده از وعده‌ها
  • Web Workers — اجرای کد در پس‌زمینه
SynchronousAsynchronousTask 1 (3s)Task 2 (2s)Task 3 (1s)Total: 6s (blocking)Task 1 startedTask 2 started (parallel)Task 3 started (parallel)Total: 3s (non-blocking)

آشنایی با جاوا اسکریپت ناهمزمان / Asynchronous

در دنیای برنامه نویسی، به ویژه در توسعه وب، جاوا اسکریپت ناهمزمان سنگ بنای است که به ما امکان می دهد تا کارهایی را که ممکن است مدتی طول بکشد – مانند فچ کردن داده ها از یک API خارجی – بدون فریز کردن رابط کاربری انجام دهیم. این مقاله به بررسی چیستی جاوا اسکریپت ناهمزمان می‌پردازد، چرا به آن نیاز است، و روش‌های مدرن برای مدیریت مؤثر عملیات ناهمزمان را معرفی می‌کند.

چرا جاوا اسکریپت ناهمزمان؟

جاوا اسکریپت، ذاتاً تک رشته ای است، به این معنی که فقط می تواند یک عملیات را در هر زمان و به ترتیب معین اجرا کند. این مشخصه می تواند منجر به مشکلات عملکردی در هنگام اجرای وظایفی شود که تکمیل آنها زمان قابل توجهی را می طلبد، مانند دانلود یک تصویر یا جستجو در پایگاه داده. این عملیات مانع از اجرای جاوا اسکریپت بعدی می شود که منجر به تجربه کاربری ضعیف می شود.

جاوا اسکریپت ناهمزمان با اجازه دادن به اجرای وظایف طولانی مدت در پس‌زمینه، این مشکل را برطرف می‌کند و موتور جاوا اسکریپت را قادر می‌سازد تا کارهای دیگر را در حالی که منتظر تکمیل عملیات ناهمزمان است، اجرا کند.

تکامل جاوا اسکریپت ناهمزمان

در ابتدا جاوا اسکریپت عملیات ناهمزمان را از طریق توابع پاسخ به تماس انجام می داد. با این حال، این رویکرد اغلب به «جهنم پاسخ به تماس» منجر می‌شد، جایی که تماس‌های تودرتو خواندن و نگهداری کد را دشوار می‌کرد.

برای بهبود این امر، جاوا اسکریپت Promises را معرفی کرد، اشیایی که نشان دهنده تکمیل یا شکست نهایی یک عملیات ناهمزمان هستند. Promises کد ناهمزمان خواناتر را با مدیریت خطا آسان تر می دهد.

علاوه بر این، نحو ناهمزمان/انتظار معرفی شد، که باعث می‌شود کدهای ناهمزمان بیشتر شبیه کد همزمان به نظر برسند و رفتار کنند و خوانایی و قابلیت نگهداری را افزایش دهند.

جاوا اسکریپت ناهمزمان در عمل

بیایید یک سناریوی ساده را در نظر بگیریم که در آن باید داده‌های کاربر را از یک API فچ  کنیم و آن را در کنسول ثبت کنیم.

استفاده از Fetch API با Promises:

fetch('https://api.example.com/users/1')

 .then(response => response.json())

 .then(user => console.log(user))
 .catch(error => console.error("Failed to fetch user:", error));

استفاده از Async/Await for Cleaner Syntax:

async function fetchUserData() {
 try {
   const response = await fetch('https://api.example.com/users/1');
   const user = await response.json();
   console.log(user);
 } catch (error) {
   console.error("Failed to fetch user:", error);
 }
}
fetchUserData();

تمرین عملی: ایجاد یک رابط کاربری برای داده های API

بیایید با ایجاد سناریویی که در آن داده‌های کاربر را از یک API ساختگی فچ  کرده و در صفحه وب خود نمایش می‌دهیم، درک خود را از جاوا اسکریپت ناهمزمان افزایش دهیم. این تمرین به نشان دادن قدرت عملیات ناهمزمان در یک زمینه دنیای واقعی کمک خواهد کرد.

سناریو: نمایش داده های کاربر با کلیک روی دکمه

هدف ما ایجاد یک صفحه وب ساده است که با کلیک روی یک دکمه، داده‌های کاربر را از یک API خارجی فچ  و نمایش می‌دهد. ما از Fetch API در کنار syntax async/wait برای کدهای ناهمزمان تمیز و خوانا استفاده خواهیم کرد.

مرحله 1: تنظیم HTML

ساختار HTML ما شامل یک دکمه برای راه اندازی عملیات فچ کردن داده ها و یک عنصر <div> برای نمایش داده های فچ  شده است.

ساختار HTML:

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Fetch User Data</title>
</head>
<body>
   <button id="fetchButton">Fetch User Data</button>
   <div id="userData"></div>
   <script src="script.js"></script>
</body>
</html>

عنصر <button> برای شروع عملیات فچ  استفاده خواهد شد. عنصر <div> (id="userData") جایی است که داده های فچ  شده خود را نمایش خواهیم داد.

مرحله 2: اضافه کردن سبک با CSS

بیایید یک استایل اولیه اضافه کنیم تا ارائه داده هایمان کمی جذاب تر شود.

#userData {
   margin-top: 20px;
   padding: 10px;
   background-color: #f0f0f0;
   border-radius: 5px;
}

ما تعدادی حاشیه، بالشتک، رنگ پس‌زمینه و گوشه‌های گرد را برای ظرف ارائه می‌کنیم که داده‌های کاربر را نمایش می‌دهد.

مرحله 3: فچ  و نمایش داده ها با جاوا اسکریپت

حال، اجازه دهید جاوا اسکریپتی را بنویسیم که داده‌های کاربر را به صورت ناهمزمان دریافت می‌کند و آن را در داخل ظرف <div> ما نمایش می‌دهد.

document.getElementById('fetchButton').addEventListener('click', async () => {
   const userDataContainer = document.getElementById('userData');
   // Displaying a loading message
   userDataContainer.textContent = 'Loading...';
   try {
       // Fetching user data from a mock API endpoint
       const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
       const user = await response.json();
       // Displaying the fetched user name and email
       userDataContainer.textContent = `Name: ${user.name}, Email: ${user.email}`;
   } catch (error) {
       // Handling any errors that occur during the fetch operation
       userDataContainer.textContent = 'Failed to fetch user data.';
       console.error("Fetch error:", error);
   }
});
  • ما یک شنونده رویداد به دکمه خود اضافه می کنیم که با کلیک کردن فعال می شود.
  • ما از دستور async/wait برای فچ  داده‌های کاربر از یک متغیر API استفاده می‌کنیم. این رویکرد کد ما را تمیز و خوانا نگه می دارد.
  • بلوک try...catch هرگونه خطایی را که ممکن است در حین عملیات فچ  رخ دهد کنترل می‌کند و اطمینان می‌دهد که برنامه ما قوی باقی می‌ماند.

نتیجه

این سناریو قدرت جاوا اسکریپت ناهمزمان را در توسعه وب نشان می دهد. با استفاده از Fetch API با syntax async/wait، می‌توانیم درخواست‌های وب را به صورت ناهمزمان انجام دهیم و برنامه‌های وب خود را پاسخگو و سریع نگه داریم. ساختار try...catch مدیریت خطا را بیشتر می کند و کد ما را قابل اطمینان تر و نگهداری آسان تر می کند.

با API های مختلف آزمایش کنید و راه های مختلفی را برای بهبود تجربه کاربر با استفاده از جاوا اسکریپت ناهمزمان در پروژه های خود کاوش کنید.

برنامه نویسی ناهمزمان در جاوا اسکریپت توسعه برنامه های کاربردی وب سریع و پاسخگو را امکان پذیر می کند. درک و استفاده مؤثر از جاوا اسکریپت ناهمزمان، از جمله Promises و async/wait، برای توسعه دهندگان وب مدرن برای ایجاد تجربیات کاربر یکپارچه ضروری است.

Synchronous Programming

برنامه نویسی سنکرون چیست؟

برنامه نویسی همزمان در جاوا اسکریپت (یا در برنامه نویسی به طور کلی) به یک رویکرد متوالی برای اجرای کد اشاره دارد. در یک مدل برنامه نویسی همزمان، وظایف یکی پس از دیگری اجرا می شوند. هر کار قبل از اجرا منتظر می ماند تا کار قبلی تمام شود. این به این معنی است که اگر یک عملیات خاص برای کامل شدن زمان زیادی طول بکشد (مانند خواندن یک فایل از دیسک یا پرس و جو از یک پایگاه داده)، کارهای بعدی باید تا پایان عملیات منتظر بمانند، که به طور بالقوه منجر به مسدود کردن رفتار و یک برنامه یا برنامه کاربردی کمتر پاسخگو می شود.

در اینجا یک تشبیه ساده وجود دارد: تصور کنید در یک صف کافی شاپ هستید. هر مشتری قهوه خود را سفارش می دهد، منتظر می ماند تا آن را درست کند و به او تحویل دهد و تنها پس از آن مشتری بعدی سفارش خود را شروع می کند. این فرآیند خطی و متوالی است و هر مرحله قبل از ادامه در انتظار تکمیل مرحله قبلی است.

در جاوا اسکریپت، زمانی که کد همزمان را اجرا می کنید، هر دستور قبل از رفتن به دستور بعدی، اجرای خود را کامل می کند. در اینجا یک مثال اساسی برای نشان دادن اجرای کد همزمان آورده شده است:

console.log('First task starts');
// Simulate a task that takes time, for example, a loop
for(let i = 0; i < 1000000000; i++) {}
console.log('First task ends and second task starts');
// Second task
console.log('Second task ends');

در این مثال، جاوا اسکریپت قبل از رفتن به دستور بعدی console.log منتظر می ماند تا حلقه کامل شود. این رفتار تضمین می کند که عملیات ها به ترتیبی که نوشته شده اند قابل پیش بینی و اجرا می شوند. با این حال، می‌تواند منجر به ناکارآمدی شود، به‌ویژه زمانی که با عملیات‌هایی که زمان‌بر هستند یا به منابع خارجی مانند درخواست‌های شبکه وابسته هستند، سر و کار داریم.

ماهیت همزمان اجرا در چنین سناریوهایی می تواند برنامه را کند یا بی پاسخ کند، به خصوص در زمینه توسعه وب که تجربه کاربر بسیار مهم است. به همین دلیل است که الگوهای برنامه نویسی ناهمزمان اغلب برای عملیاتی که شامل تاخیر یا انتظار قابل توجه است ترجیح داده می شود و به برنامه اجازه می دهد تا با اجرای وظایف دیگر در حالی که منتظر تکمیل این عملیات است پاسخگو باقی بماند.

تفاوت کلیدی بین برنامه‌نویسی ناهمزمان و همزمان

تفاوت کلیدی بین برنامه‌نویسی ناهمزمان و همزمان در نحوه مدیریت عملیات‌هایی است که تکمیل آنها به زمان نیاز دارد، به ویژه آنهایی که شامل انتظار هستند، مانند درخواست‌های شبکه، عملیات فایل یا تایمر. در اینجا به تفکیک تفاوت ها، به دنبال یک مثال و بهترین شیوه ها آمده است:

برنامه نویسی همزمان

  • جریان اجرا: عملیات به ترتیب و یکی پس از دیگری اجرا می شوند. هر عملیات باید قبل از شروع عملیات بعدی کامل شود.
  • مسدود کردن: اگر تکمیل یک عملیات به زمان نیاز داشته باشد (مثلاً خواندن یک فایل بزرگ)، اجرای برنامه مسدود می‌شود، به این معنی که تا زمانی که عملیات فعلی تمام نشود، نمی‌توان به عملیات بعدی ادامه داد.
  • سادگی: درک و دنبال کردن آن آسان‌تر است، زیرا اجرای کد با ترتیب نوشته شدن کد مطابقت دارد.
  • موارد استفاده: بهترین گزینه برای عملیات سریع یا جاهایی که کارها باید با ترتیب خاصی بدون نیاز به همزمانی کامل شوند.

برنامه نویسی ناهمزمان

  • جریان اجرا: عملیاتی که تکمیل آنها زمان بر است جدا از جریان اصلی برنامه اجرا می شود و به برنامه اجازه می دهد تا به اجرای وظایف دیگر ادامه دهد.
  • Non-Blocking: عملیات طولانی مدت به گونه ای انجام می شود که اجرای برنامه را مسدود نمی کند. سایر عملیات ها همچنان می توانند در حین انتظار برای نتیجه اجرا شوند.
  • پیچیدگی: به دلیل تماس‌های برگشتی، وعده‌ها و دستور ناهمگام/انتظار می‌تواند برای درک و پیاده‌سازی پیچیده‌تر باشد. مدیریت خطا و اشکال زدایی نیز می تواند چالش برانگیزتر باشد.
  • موارد استفاده: ایده آل برای عملیاتی که شامل انتظار است، مانند درخواست های وب API، ورودی/خروجی فایل یا هر گونه ارتباط شبکه ای. برای افزایش عملکرد و پاسخگویی در برنامه ها عالی است.

مثال

همزمان / Synchronous:

console.log('Start downloading image...');
// Simulate image download (blocking)
for(let i = 0; i < 1000000000; i++) {}
console.log('Image downloaded.');
// This next line has to wait until the image download simulation is complete
console.log('Start processing image...');

نامتقارن / Asynchronous:

console.log('Start downloading image...');
setTimeout(() => {
   // Simulate image download (non-blocking)
   console.log('Image downloaded.');
}, 2000);
// This next line runs immediately, without waiting for the image download simulation
console.log('Start processing image...');

Best Practices Asynchronous

  • از متدهای ناهمزمان برای عملیات ورودی/خروجی استفاده کنید: برای عملیات‌هایی مانند درخواست‌های شبکه، وظایف سیستم فایل، و هرگونه عملیات محدود به ورودی/خروجی، از متدهای ناهمزمان برای جلوگیری از مسدود کردن رشته اصلی اجرا استفاده کنید، در نتیجه پاسخ‌دهی و عملکرد برنامه‌تان را بهبود می‌بخشید.
  • Prefer Promises and Async/Await Over Callbacks: Callbacks می تواند منجر به جهنم برگشت به تماس شود و خواندن و نگهداری کد را سخت کند. Promises و syntax async/wait متدی تمیزتر و خواناتر برای مدیریت عملیات ناهمزمان ارائه می دهد.
  • مدیریت خطا: از بلوک‌های try/catch با async/wait استفاده کنید تا خطاها را به خوبی مدیریت کنید. هنگام استفاده از وعده‌ها، مطمئن شوید که خطاها را شناسایی کنید تا از کنترل نشدن آنها جلوگیری کنید.
  • همزمانی: برای بهینه‌سازی عملکرد، از ویژگی‌هایی مانند Promise.all برای اجرای چندین عملیات ناهمزمان به‌طور همزمان و نه متوالی استفاده کنید.
  • درک زمان و نحوه استفاده از برنامه نویسی ناهمزمان و همزمان برای توسعه برنامه های کاربردی جاوا اسکریپت کارآمد، پاسخگو و قابل نگهداری بسیار مهم است.

Event Handlers چیست؟

کنترل‌کننده‌های رویداد، توابعی در جاوا اسکریپت هستند که در پاسخ به رویدادهای خاصی که در یک برنامه وب رخ می‌دهند، اجرا می‌شوند. این رویدادها می‌تواند هر چیزی باشد، از کلیک کاربر روی دکمه، ارسال فرم، حرکت دادن ماوس تا رویدادهای پیچیده‌تر ایجاد شده توسط سیستم. کنترل‌کننده‌های رویداد نقش مهمی در تعاملی کردن صفحات وب و پاسخگویی به اقدامات کاربر دارند.

نقش آنها در جاوا اسکریپت

  • تعامل کاربر: آنها صفحات وب را با اجرای کد در پاسخ به اقدامات کاربر تعاملی می کنند.
  • اجرای ناهمزمان: کنترل کننده های رویداد معمولاً به صورت ناهمزمان اجرا می شوند و به رویدادهایی که در زمان های نامشخص رخ می دهند پاسخ می دهند.
  • دستکاری DOM: آنها اغلب مدل شیء سند (DOM) را بر اساس اقدامات کاربر، مانند نمایش یا پنهان کردن عناصر، اصلاح محتوا و غیره دستکاری می کنند.
  • اعتبار سنجی و فرم‌ها: کنترل‌کننده‌های رویداد برای اعتبارسنجی ورودی‌های فرم قبل از ارسال استفاده می‌شوند.
  • انیمیشن و جلوه ها: آنها انیمیشن ها یا جلوه های بصری را در پاسخ به تعاملات کاربر ایجاد می کنند.


سنکرون یا ناهمزمان؟

کنترل‌کننده‌های رویداد طبیعتاً ناهمزمان هستند، زیرا منتظر می‌مانند تا یک رویداد بدون مسدود کردن اجرای کدهای دیگر اتفاق بیفتد. تابع handler تنها زمانی فراخوانی می شود که رویداد مرتبط با آن فعال شود.

مثال عملی: یک رویداد کلیک ساده

بیایید یک مثال ساده ایجاد کنیم که در آن کلیک کردن روی یک دکمه رنگ یک عنصر div را تغییر می‌دهد.

مرحله 1: ساختار HTML

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Event Handler Example</title>
   <link rel="stylesheet" href="style.css">
</head>
<body>
   <button id="changeColorBtn">Change Color</button>
   <div id="colorBox"></div>
   <script src="script.js"></script>
</body>
</html>

مرحله 2: CSS برای استایل کردن

/* style.css */

#colorBox {
   width: 100px;
   height: 100px;
   background-color: blue;
}
button {
   margin: 20px;
   padding: 10px;
}

مرحله 3: جاوا اسکریپت برای مدیریت رویداد

// script.js
document.addEventListener("DOMContentLoaded", function() {
   // Selecting the button and the div
   const button = document.getElementById('changeColorBtn');
   const box = document.getElementById('colorBox');
   // Defining the event handler function
   function changeColor() {
       box.style.backgroundColor = box.style.backgroundColor === 'blue' ? 'red' : 'blue';
   }
   // Assigning the event handler to button's click event
   button.addEventListener('click', changeColor);
});

این کد قبل از انتخاب عناصر و تخصیص کنترل کننده رویداد تا بارگذاری کامل DOM صبر می کند. هنگامی که دکمه کلیک می شود، تابع changeColor اجرا می شود و رنگ پس زمینه div را بین آبی و قرمز تغییر می دهد.

بهترین توصیه ها برای استفاده از رویداد Handlers

  • از addEventListener استفاده کنید: به شما امکان می دهد چندین کنترل کننده رویداد را به یک رویداد اضافه کنید و انعطاف پذیری بیشتری را نسبت به ویژگی onclick قدیمی در HTML فراهم می کند.
  • جداسازی نگرانی ها: کدهای HTML، CSS و جاوا اسکریپت خود را جدا نگه دارید تا کدهای تمیز و قابل مدیریت را حفظ کنید.
  • توابع ناشناس: برای مدیریت رویدادهای ساده و یکبار مصرف، استفاده از توابع ناشناس را مستقیماً در داخل addEventListener در نظر بگیرید.
  • Remove Event Listeners: اگر دیگر نیازی به شنونده رویداد نیست، آن را با removeEventListener حذف کنید تا از نشت حافظه جلوگیری شود.
  • Delegation رویداد: از تفویض رویداد برای به حداقل رساندن تعداد گردانندگان رویداد استفاده کنید. به وقایع مربوط به یک والدین مشترک به جای فرزندان فردی گوش دهید.
  • دسترس‌پذیری: اطمینان حاصل کنید که کنترل‌کننده‌های رویداد در دسترس‌پذیری وب‌سایت شما دخالت نمی‌کنند. کاربران صفحه‌کلید و صفحه‌خوان باید بتوانند با همه عناصر تعاملی تعامل داشته باشند.

مدیریت رویدادها ستون فقرات توسعه وب تعاملی هستند که به توسعه دهندگان اجازه می دهند وب سایت های پاسخگو و کاربر پسند ایجاد کنند.

آشنایی با Promises در جاوا اسکریپت

وعده ها /  Promise برای مدیریت عملیات ناهمزمان در جاوا اسکریپت اساسی هستند. آنها یک مکانیسم قوی برای مدیریت نتایج و خطاهای عملیات ناهمزمان، مانند درخواست‌های شبکه، عملیات فایل یا تایمر ارائه می‌دهند. بیایید وعده ها، موارد استفاده آنها و نحوه استفاده از آنها را با مثال های عملی بررسی کنیم.

Promise در جاوا اسکریپت یک شی / Object است که نشان دهنده تکمیل یا شکست نهایی یک عملیات ناهمزمان / Asynchronous است. این به شما اجازه می دهد تا کنترل کننده ها را با ارزش موفقیت یا دلیل شکست یک اقدام ناهمزمان مرتبط کنید. این به متدهای ناهمزمان اجازه می‌دهد مقادیری مانند متدهای همزمان را برگردانند: به جای اینکه بلافاصله مقدار نهایی را برگرداند، متد ناهمزمان وعده‌ای را برای ارائه مقدار در نقطه‌ای در آینده برمی‌گرداند.

در کجا از وعده ها استفاده می شود؟

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

  • فچ کردن داده ها از سرور با استفاده از Fetch API.
  • انجام عملیات فایل در Node.js.
  • بسته بندی API های قدیمی مبتنی بر تماس در وعده هایی برای قابلیت استفاده و کنترل بهتر.

چرا وعده ها مهم هستند؟

  • غیر مسدود کردن: Promises به جاوا اسکریپت اجازه می دهد تا عملیات طولانی را در پس زمینه انجام دهد و پاسخگویی برنامه های وب را بهبود بخشد.
  • مدیریت خطای ساده شده: با .then() برای موفقیت و .catch() برای خطاها، وعده ها فرآیند کشف و رسیدگی به خطاها را ساده می کنند.
  • Cleaner Async Code: وعده‌ها پیچیدگی کدهای ناهمزمان را کاهش می‌دهند، از جهنم بازگشت به تماس بدنام جلوگیری می‌کنند و خواندن و نگهداری کد را آسان‌تر می‌کنند.

نحوه استفاده از وعده ها

وعده‌ها در جاوا اسکریپت بر اساس کمیت طبقه‌بندی نمی‌شوند، بلکه بر اساس وضعیت و کاربردشان دسته‌بندی می‌شوند. با این حال، چندین مفهوم و روش کلیدی مرتبط با وعده ها وجود دارد که باید با آنها آشنا باشید. در اینجا یک تفکیک وجود دارد:

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

  1. در انتظار / Pending: حالت اولیه یک قول. نتیجه هنوز مشخص نشده است زیرا عملیات ناهمزمان که وعده نشان می دهد کامل نشده است.
  1. برآورده شده / Fulfilled: حالت یک وعده که نشان دهنده یک عملیات موفق است. این به این معنی است که عملیات ناهمزمان کامل شده است و وعده اکنون یک مقدار حل شده دارد.
  1. Rejected: حالت یک وعده که نشان دهنده یک عملیات ناموفق است. این به این معنی است که عملیات ناهمزمان شکست خورده است و قول اکنون دلیلی برای شکست دارد.
  1. روش های اصلی وعده

چندین روش در خود شی Promise یا یک نمونه قول وجود دارد. درک این موارد به شما کمک می کند تا به طور موثر با وعده ها کار کنید:

  • (Promise Constructor () (new Promise: برای ایجاد یک وعده جدید استفاده می شود. سازنده یک تابع مجری می گیرد که با دو تابع Resol و Reject فراخوانی می شود که به ترتیب برای حل یا رد قول استفاده می شود.
  • ()then: برای برنامه‌ریزی یک callback استفاده می‌شود تا زمانی که وعده محقق شد، اجرا شود. همچنین می‌توانید برای انجام عملیات ناهمزمان اضافی به ترتیب، روش‌ها را زنجیره‌ای کنید.
  • ()catch: برای برنامه‌ریزی یک تماس برگشتی برای اجرای زمانی که وعده رد می‌شود استفاده می‌شود. برای رسیدگی به خطا در زنجیره های وعده استفاده می شود.
  • ()final: به شما امکان می دهد بدون توجه به سرنوشت وعده، یک تماس برگشتی را اجرا کنید. این برای پاکسازی منابع یا سایر کارهای نهایی مفید است.

روش های سودمند برای وعده های متعدد

این‌ها روش‌های ایستا در سازنده Promise هستند که در برخورد با وعده‌های متعدد کمک می‌کنند:

  • ()Promise.all: یک تکرار از وعده‌ها را می‌گیرد و یک وعده واحد را برمی‌گرداند که زمانی حل می‌شود که همه وعده‌های ورودی حل شوند یا زمانی که تکرار شونده حاوی هیچ وعده‌ای نباشد. رد می کند به دلیل قول اولی که رد می کند.
  • ()Promise.allSettled: شبیه Promise.all است، اما منتظر می ماند تا تمام وعده های ورودی بدون توجه به رد شدن آنها تکمیل شود. با آرایه ای از اشیاء حل می شود که هر کدام نتیجه هر وعده را توصیف می کند.
  • ()Promise.race: یک وعده تکراری را می گیرد و یک وعده واحد را برمی گرداند که به محض اینکه یکی از وعده های ورودی تسویه شد (یا حل شود یا رد شود)، با ارزش یا دلیل آن قول تسویه می شود.
  • ()Promise.any: مشابه Promise.race، اما تنها پس از رد شدن همه وعده‌های ورودی، با یک AggregateError حاوی دلایل رد شدن، رد می‌شود. به محض رفع هر یک از وعده های ورودی حل می شود.
  • ()Promise.resolve: قولی را برمی گرداند که با مقدار داده شده حل شده است. اگر مقدار یک وعده باشد، آن وعده برگردانده می شود.
  • ()Promise.reject: قولی را که با دلیل ذکر شده رد شده است برمی گرداند.

Promises یک ویژگی قدرتمند جاوا اسکریپت برای مدیریت عملیات ناهمزمان است. آنها روشی قوی‌تر برای مدیریت کد ناهمزمان در مقایسه با رویکردهای قدیمی‌تر مبتنی بر تماس ارائه می‌کنند. درک وضعیت‌هایی که یک وعده می‌تواند در آن باشد، و همچنین روش‌های موجود برای مدیریت وعده‌ها، برای برنامه‌نویسی موثر جاوا اسکریپت، به ویژه هنگام کار با APIهای وب مدرن و الگوهای کد ناهمزمان، بسیار مهم است.

ایجاد و استفاده از وعده ها

یک وعده نشان دهنده تکمیل (failure) نهایی یک عملیات ناهمزمان و مقدار حاصل از آن است. در اینجا نحوه استفاده از آنها آمده است:

ایجاد یک Promise: یک وعده با سازنده Promise که یک تابع مجری می گیرد، نمونه سازی می شود. این تابع بلافاصله توسط اجرای Promise اجرا می شود و دو تابع را به عنوان پارامتر دریافت می کند که به طور سنتی به نام های Resol و Reject نامیده می شود.

const myPromise = new Promise((resolve, reject) => {
 // Asynchronous operation here
 if (/* operation successful */) {
   resolve(value); // Resolves the promise with value
 } else {
   reject(error); // Rejects the promise with error
 }
});

استفاده از Promises: هنگامی که یک قول دارید، می‌توانید تماس‌های برگشتی را برای رسیدگی به تحقق (then.) یا رد (catch.) به آن متصل کنید.

myPromise.then((value) => {
 // Success!
}).catch((error) => {
 // An error occurred
});

فچ کردن - Fetch

بیایید از Fetch API برای فچ کردن داده ها از یک URL استفاده کنیم و ببینیم چگونه وعده ها عملیات ناهمزمان را ساده می کنند.

// Fetching a list of products from the given URL
fetch("https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json")

 .then(response => response.json()) // Parsing the response to JSON

 .then(data => console.log(data)) // Logging the data

 .catch(error => console.error('Fetching error:', error)); // Handling any errors

در این مثال، fetch() یک وعده را برمی گرداند، که به ما امکان می دهد .then() را برای سناریوهای موفقیت و .catch() را برای مدیریت خطاها زنجیره کنیم.

مدیریت چندین عملیات ناهمزمان

گاهی اوقات، ممکن است لازم باشد چندین عملیات را انجام دهید که به نتایج یکدیگر بستگی ندارد. در چنین مواردی، Promise.all() می تواند فوق العاده مفید باشد زیرا منتظر می ماند تا همه وعده ها حل و فصل شوند.

Promise.all([
 fetch("https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/not-found"),
 fetch("https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json")
]).then(responses => {
 // responses is an array of the response objects from the above URLs
 responses.forEach(response => console.log(response.url, response.status));
}).catch(error => {
 console.error('One or more requests failed:', error);
});

این مثال تلاش می کند تا داده ها را از دو URL فچ  کند. Promise.all() تضمین می‌کند که ما فقط زمانی ادامه می‌دهیم که تمام درخواست‌های فچ  تکمیل شوند و به ما امکان می‌دهد نتایج را به صورت جمعی مدیریت کنیم.

ایجاد یک وعده جدید:

const myPromise = new Promise((resolve, reject) => {
 // Perform some asynchronous operation here
 const success = true; // Just a placeholder condition
 if (success) {
   resolve('Operation succeeded');
 } else {
   reject('Operation failed');
 }
});
// Using the created promise

myPromise

 .then(result => console.log(result)) // Logs 'Operation succeeded' if successful

 .catch(error => console.error(error)); // Logs 'Operation failed' if an error occurred

در این کد، myPromise یک شی جدید Promise است. تابع executor در new Promise() بلافاصله اجرا می شود و قرار است در نهایت حل (برای موفقیت) یا reject (برای شکست) را فراخوانی کند.

نتیجه گیری و بهترین شیوه ها

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

  • هنگام رسیدگی به وعده‌ها، از async/wait برای نحو تمیزتر استفاده کنید.
  • همیشه با استفاده از .catch () یا try/catch با async/await، رد وعده ها را مدیریت کنید.
  • از Promise.all() برای عملیات ناهمزمان همزمان که به یکدیگر وابسته نیستند استفاده کنید.

مثال: فچ کردن داده های محصول

در این سناریو، ما یک برنامه وب ساده ایجاد می کنیم که داده های محصول را از URL ارائه شده فچ  می کند و آن را در صفحه نمایش می دهد. ما از ویژگی‌های جاوا اسکریپت مدرن مانند فچ  برای بازیابی داده‌ها، همگام‌سازی/انتظار برای عملیات ناهمزمان و الگوهای واقعی برای تولید HTML استفاده خواهیم کرد. این برنامه همچنین با CSS برای ارائه جذاب‌تر استایل‌بندی می‌شود.

مرحله 1: ساختار HTML را تنظیم کنید

ابتدا یک فایل HTML ایجاد می کنیم که شامل یک محفظه برای محصولات و یک تگ اسکریپت برای گنجاندن کد جاوا اسکریپت ما است.

index.html
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Product Display</title>
   <link rel="stylesheet" href="style.css">
</head>
<body>
   <h1>Product Listings</h1>
   <div id="products"></div>
   <script src="script.js"></script>
</body>
</html>

مرحله 2: یک سبک CSS

یک فایل style.css ایجاد کنید.

body {
   font-family: Arial, sans-serif;
   padding: 20px;
   background-color: #f4f4f4;
}
h1 {
   color: #333;
}
.products {
   display: flex;
   flex-wrap: wrap;
   gap: 20px;
}
.product {
   background: white;
   padding: 10px;
   box-shadow: 0 2px 4px rgba(0,0,0,0.1);
   width: calc(33.333% - 20px);
   border-radius: 5px;
}
.product img {
   max-width: 100%;
   border-radius: 5px;
}
.product h2 {
   font-size: 18px;
   color: #333;
}
.product p {
   font-size: 16px;
   color: #666;
}

مرحله 3: فچ  و نمایش محصولات با جاوا اسکریپت

در نهایت، ما script.js را برای فچ کردن داده های محصول و نمایش هر محصول در صفحه HTML پیاده سازی می کنیم.

async function fetchProducts() {
   try {
       const response = await fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');
       if (!response.ok) {
           throw new Error(`HTTP error: ${response.status}`);
       }
       const data = await response.json();
       displayProducts(data);
   } catch (error) {
       console.error(`Could not get products: ${error}`);
   }
}
function displayProducts(products) {
   const container = document.getElementById('products');
   products.forEach(product => {
       const productElement = document.createElement('div');
       productElement.className = 'product';
       productElement.innerHTML = `
           <img src="https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/${product.image}" alt="${product.name}">
           <h2>${product.name}</h2>
           <p>$${product.price}</p>
       `;
       container.appendChild(productElement);
   });
}
// Call fetchProducts to load and display the products
fetchProducts();

توضیح کد جاوا اسکریپت:

  • تابع Async fetchProducts: این تابع به طور ناهمزمان داده های محصول را از URL داده شده با استفاده از fetch فچ می کند. از انتظار برای تکمیل فچ استفاده می‌کند، که باعث می‌شود کد هماهنگ به نظر برسد و خواندن آن آسان‌تر شود.
  • مدیریت خطا: اگر درخواست فچ ناموفق باشد (خطای شبکه یا وضعیت پاسخ درست نیست)، یک خطا پرتاب می‌شود و در بلوک catch ثبت می‌شود، جایی که در کنسول ثبت می‌شود.
  • نمایش محصولات: هنگامی که داده ها با موفقیت فچ شد و به JSON تبدیل شد، displayProducts با داده های محصول فچ شده فراخوانی می شود. این تابع بر روی هر محصول تکرار می شود، یک عنصر HTML جدید برای هر یک ایجاد می کند و آن را در DOM قرار می دهد. تصویر، نام و قیمت محصول نمایش داده می شود.
  • فراخوانی fetchProducts: در نهایت، ما fetchProducts را فراخوانی می کنیم تا هنگام بارگیری اسکریپت، عملیات فچ را آغاز کنیم.

این مثال نحوه استفاده از ویژگی‌های جاوا اسکریپت مدرن را برای فچ داده‌ها از یک API راه دور و نمایش آن در یک صفحه وب نشان می‌دهد. ما از async/wait برای کدهای ناهمزمان با خوانایی آسان و الفبای الگو برای تولید پویا HTML بر اساس داده‌های فچ شده استفاده کردیم. استفاده از CSS flexbox تضمین می کند که محصولات به خوبی در صفحه چیده شوند.

PendingresolvedrejectedFulfilledRejected.then(result) runs.catch(error) runs

مقدمه ای بر Web Workers

در توسعه وب مدرن، حفظ یک رابط کاربری روان و پاسخگو در حین انجام عملیات پیچیده می تواند چالش برانگیز باشد. جاوا اسکریپت که تک رشته ای است، در همان رشته رابط کاربری اجرا می شود و آن را مستعد مسدود کردن عملیاتی می کند که می تواند صفحه را مسدود کند. اینجاست که Web Workers وارد عمل می‌شود و راه‌حلی قدرتمند را با فعال کردن وظایف به موازات موضوع اصلی ارائه می‌دهد.

Web Workers چیست؟

Web Workers به شما این امکان را می دهد که عملیات جاوا اسکریپت را در رشته های پس زمینه اجرا کنید و رشته اصلی را برای تعاملات UI آزاد نگه دارید. این اجرای موازی تضمین می کند که وظایف سنگین رابط کاربری را مسدود نمی کند و پاسخگویی برنامه های وب را بهبود می بخشد.

انواع کارگران - Workers

کارگران اختصاص داده شده: کارگران متعهد که به خالق خود گره خورده اند، فقط می توانند با رشته ای که آنها را ایجاد کرده ارتباط برقرار کنند، و آنها را برای تخلیه وظایف خاص و فشرده از رشته اصلی ایده آل می کند.

Shared Workers: این کارگران را می توان با چندین اسکریپت – حتی در پنجره ها یا برگه های مختلف مرورگر – که تعاملات پیچیده تر و به اشتراک گذاری داده ها را تسهیل می کند، قابل دسترسی است.

Service Workers: به عنوان یک پروکسی شبکه عمل می‌کند، سرویس‌کاران تجربه‌های آفلاین غنی، همگام‌سازی پس‌زمینه، و اعلان‌های فشاری را در میان چیزهای دیگر فعال می‌کنند. آنها نقش مهمی در توسعه برنامه های وب پیشرو (PWA) دارند.

مثال عملی: Web Workers

بیایید یک مثال مدرن و پاسخگو ایجاد کنیم که استفاده از Web Workers را در یک برنامه وب نشان می دهد. ما یک برنامه ساده خواهیم ساخت که اعداد فیبوناچی را در پس زمینه محاسبه می کند. هدف حفظ یک رابط کاربری پاسخگو حتی در حین انجام وظایف محاسباتی فشرده است.

سناریو: ماشین حساب فیبوناچی با Web Worker

این اپلیکیشن به کاربران اجازه می دهد تا عددی را وارد کنند و عدد فیبوناچی مربوطه را در پس زمینه با استفاده از Web Worker محاسبه می کند. دنباله فیبوناچی به دلیل سادگی انتخاب شده است و به دلیل اینکه محاسبه آن می تواند برای ورودی های بزرگ منابع فشرده باشد، و آن را برای این نمایش ایده آل می کند.

مرحله 1: ساختار پروژه

یک پوشه پروژه با فایل های زیر ایجاد کنید:

  • index.html - ساختار HTML برنامه ما.
  • style.css - برای استایل دادن به برنامه ما.
  • main.js - فایل اصلی جاوا اسکریپت برای تعامل رابط کاربری.
  • fibonacciWorker.js - اسکریپت Web Worker که اعداد فیبوناچی را محاسبه می کند.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Prime Number Generator</title>
   <link rel="stylesheet" href="style.css">
</head>
<body>
   <h1>Fibonacci Calculator</h1>
   <input type="number" id="numberInput" placeholder="Enter a number">
   <button id="calculateBtn">Calculate</button>
   <div id="result">Result will appear here...</div>
   <script src="main.js"></script>
</body>
</html>

Styling (style.css)

body {
   font-family: Arial, sans-serif;
   text-align: center;
   margin-top: 50px;
}
input, button {
   font-size: 18px;
   padding: 10px;
   margin: 5px;
}
#result {
   margin-top: 20px;
   font-size: 20px;
   color: green;
}

منطق موضوع اصلی (main.js)

// Initialize the Web Worker
const worker = new Worker('fibonacciWorker.js');
document.getElementById('calculateBtn').addEventListener('click', function() {
   const number = document.getElementById('numberInput').value;
   worker.postMessage(number); // Send the number to the worker
   document.getElementById('result').textContent = 'Calculating...';
});
// Listen for messages from the worker
worker.addEventListener('message', function(event) {
   document.getElementById('result').textContent = `Result: ${event.data}`;
});
// Error handling
worker.addEventListener('error', function(event) {
   document.getElementById('result').textContent = `Error: ${event.message}`;
});

Worker Thread Logic - منطق موضوع کارگر (fibonacciWorker.js)

self.addEventListener('message', function(event) {
   const number = event.data;
   const result = calculateFibonacci(number);
   self.postMessage(result);
});
function calculateFibonacci(n) {
   if (n <= 1) return n;
   return calculateFibonacci(n - 1) + calculateFibonacci(n - 2);
}

توضیحات گام به گام:

  • راه اندازی HTML: ما یک رابط کاربری ساده با ورودی برای عدد، یک دکمه برای شروع محاسبه و یک div برای نمایش نتیجه تعریف می کنیم.
  • CSS Styling: برای بهبود خوانایی و قابلیت استفاده، یک ظاهر طراحی اولیه را برای عناصر UI ما ارائه می دهد.
  • Main Thread Logic: Web Worker را راه اندازی می کند و شنوندگان رویداد را برای عناصر UI تنظیم می کند. شماره ورودی را برای پردازش به کارگر می فرستد و نتیجه یا پیام های خطای دریافتی از کارگر را نمایش می دهد.
  • Web Worker Logic: به پیام هایی از رشته اصلی گوش می دهد، محاسبه فیبوناچی را انجام می دهد و نتیجه را به رشته اصلی ارسال می کند.

بهترین توصیه ها:

  • موارد استفاده: Web Workers برای کارهایی مانند پردازش داده ها، محاسبات پیچیده و سایر عملیاتی که در غیر این صورت رشته UI را مسدود می کنند، ایده آل هستند.
  • ارتباطات: کارگران از طریق پیام‌ها با موضوع اصلی ارتباط برقرار می‌کنند و از کپسوله‌سازی داده‌ها و ایمنی رشته اطمینان می‌دهند.
  • محدودیت‌ها: کارگران به DOM دسترسی ندارند و در یک زمینه محدود اجرا می‌شوند، که آنها را برای محاسبات خارج از رشته UI مناسب می‌کند.
  • از Web Workers برای محاسبات فشرده برای پاسخگو نگه داشتن رابط کاربری استفاده کنید.
  • کارگران را زمانی که دیگر برای آزاد کردن منابع لازم نیست خاتمه دهید: worker.terminate();.
  • مدیریت خطا: اطمینان حاصل کنید که خطاها را هم در اسکریپت اصلی و هم در اسکریپت کارگر مدیریت می کنید.
  • بهینه سازی الگوریتم ها برای عملکرد بهتر، به ویژه در کارگران برای به حداقل رساندن زمان محاسبات.

این مثال نشان می‌دهد که چگونه می‌توان از Web Workers برای انجام محاسبات پیچیده بدون مسدود کردن UI استفاده کرد و تجربه کاربری روان را تضمین کرد.

Web Workers یک مدل قوی برای اجرای وظایف فشرده بدون به خطر انداختن تجربه کاربر ارائه می‌دهد و آنها را به ابزاری ضروری در جعبه ابزار توسعه‌دهندگان وب مدرن تبدیل می‌کند.

همانطور که به پایان سفر خود از طریق جنبه های اساسی HTML، CSS و جاوا اسکریپت می رسیم، مهم است که در مسیری که طی کرده ایم فکر کنیم. از اصول ساختاردهی صفحات وب با HTML گرفته تا سبک سازی آنها با CSS، و در نهایت به افزودن تعامل با جاوا اسکریپت، ما طیف گسترده ای از اصول توسعه وب را پوشش داده ایم که برای هر توسعه دهنده وب مشتاق ضروری است.

اما سفر به اینجا ختم نمی شود. دنیای توسعه وب بسیار گسترده و دائماً در حال تغییر است و استانداردها، چارچوب‌ها و بهترین شیوه‌های جدید همیشه در حال ظهور هستند. دانشی که به دست آورده اید یک پایه محکم است، اما یادگیری و تمرین مستمر کلید پیشرفت مهارت های شما و به روز ماندن در این زمینه پویا است.

برای کسانی که مشتاق به کاوش عمیق تر و گسترش درک خود از توسعه وب هستند، منابع فراوانی در انتظار است. یک منبع با ارزش ویژه، اسناد وب شبکه توسعه دهنده موزیلا (MDN) است:

MDN Web Docs - JavaScript را یاد بگیرید

MDN Web Docs مخزن گسترده ای از دانش است که آموزش های عمیق، راهنماها و مواد مرجع در HTML، CSS، جاوا اسکریپت و موارد دیگر را ارائه می دهد. چه به دنبال تعمیق درک خود از جاوا اسکریپت، کاوش در موضوعات پیشرفته، یا یادگیری در مورد آخرین فن آوری های وب و API ها هستید، MDN Web Docs یک منبع ضروری است.

به یاد داشته باشید، سفر یادگیری توسعه وب یکی از کاوش و کشف مداوم است. به طور منظم تمرین کنید، پروژه بسازید، و از آزمایش کردن و اشتباه کردن نترسید – همه اینها بخشی از فرآیند یادگیری است. با جامعه توسعه‌دهنده تعامل داشته باشید، در پروژه‌های منبع باز مشارکت داشته باشید و مرزهای آنچه را که می‌توانید ایجاد کنید ادامه دهید.

مهارت‌هایی که توسعه داده‌اید، تازه شروع کار هستند. با فداکاری، خلاقیت و اشتیاق به یادگیری، شما در راه تبدیل شدن به یک توسعه دهنده وب ماهر هستید. وب بوم نقاشی شماست - پیش بروید و ایجاد کنید!

از اینکه با ما این سفر آموزشی را آغاز کردید متشکریم. در اینجا موفقیت مداوم شما در دنیای توسعه وب است!

نتیجه

همانطور که به پایان سفر خود از طریق جنبه‌های اساسی HTML، CSS و جاوا اسکریپت می‌رسیم، مهم است که در مسیری که طی کرده‌ایم تأمل کنیم. از درک بلوک‌های سازنده وب تا کاوش در پویایی جاوا اسکریپت، پایه‌ای محکم برای توسعه وب ساخته‌ایم.

اما سفر به اینجا ختم نمی‌شود. دنیای توسعه وب بسیار گسترده و دائماً در حال تغییر است. برای کسانی که مشتاق به کاوش عمیق‌تر هستند، منابع فراوانی در انتظار است.

MDN Web Docs مخزن گسترده‌ای از دانش است که آموزش‌های عمیق، راهنماها و مواد مرجع در HTML، CSS، جاوا اسکریپت و موارد دیگر ارائه می‌دهد. یک منبع ارزشمند ویژه برای هر توسعه‌دهنده وب.

MDN Web Docs — JavaScript را یاد بگیرید ←

به یاد داشته باشید، سفر یادگیری توسعه وب یکی از کاوش و کشف مدام است. به طور منظم تمرین کنید، پروژه بسازید، و از آزمون و خطا نترسید. هر خط کدی که می‌نویسید گامی به جلو است.

آزمون / Quiz — آنچه یاد گرفتید

سوال ۱: تفاوت اصلی بین برنامه‌نویسی همزمان و ناهمزمان چیست؟+
هیچ تفاوتی وجود ندارد
✅ در همزمان، هر تسک باید کامل شود قبل از شروع بعدی (مسدودکننده). در ناهمزمان، تسک‌ها می‌توانند موازی اجرا شوند بدون مسدود کردن کد
ناهمزمانی برای عملیات زمان‌بر مانند fetch و timers ضروری است.
ناهمزمان فقط برای محاسبات ریاضی استفاده می‌شود
سوال ۲: سه حالت (state) یک Promise کدامند؟+
start، running، stop
✅ pending (در انتظار)، fulfilled (محقق شده)، rejected (رد شده)
pending حالت اولیه است. پس از تکمیل، به fulfilled یا rejected تغییر می‌کند.
open، closed، error
سوال ۳: Web Workers چه کاربردی دارد؟+
برای طراحی صفحات وب استفاده می‌شود
برای ذخیره داده‌های محلی در مرورگر
✅ برای اجرای اسکریپت‌ها در یک thread جداگانه در پس‌زمینه، بدون مسدود کردن رابط کاربری
Web Workers برای عملیات سنگین محاسباتی مناسب است.