قدرت تکرارگرهای ناهمزمان جاوا اسکریپت را برای پردازش کارآمد و زیبای جریانها آزاد کنید. یاد بگیرید چگونه جریانهای داده ناهمزمان را به طور مؤثر مدیریت کنید.
تکرارگرهای ناهمزمان (Async Iterators) در جاوا اسکریپت: راهنمای جامع پردازش جریان
در دنیای توسعه مدرن جاوا اسکریپت، مدیریت جریانهای داده ناهمزمان یک نیاز مکرر است. چه در حال واکشی داده از یک API باشید، چه پردازش رویدادهای زنده، یا کار با مجموعه دادههای بزرگ، مدیریت کارآمد دادههای ناهمزمان برای ساخت برنامههای پاسخگو و مقیاسپذیر حیاتی است. تکرارگرهای ناهمزمان جاوا اسکریپت یک راه حل قدرتمند و زیبا برای مقابله با این چالشها ارائه میدهند.
تکرارگرهای ناهمزمان (Async Iterators) چه هستند؟
تکرارگرهای ناهمزمان یک ویژگی مدرن در جاوا اسکریپت هستند که به شما امکان میدهند روی منابع داده ناهمزمان، مانند جریانها یا پاسخهای API ناهمزمان، به شیوهای کنترلشده و متوالی تکرار کنید. آنها شبیه به تکرارگرهای معمولی هستند، اما با این تفاوت کلیدی که متد next()
آنها یک Promise را برمیگرداند. این به شما امکان میدهد با دادههایی که به صورت ناهمزمان میرسند کار کنید بدون اینکه رشته اصلی (main thread) مسدود شود.
یک تکرارگر معمولی را راهی برای دریافت آیتمها از یک مجموعه به صورت تک به تک در نظر بگیرید. شما آیتم بعدی را درخواست میکنید و بلافاصله آن را دریافت میکنید. از سوی دیگر، یک تکرارگر ناهمزمان مانند سفارش آنلاین کالا است. شما سفارش را ثبت میکنید (متد next()
را فراخوانی میکنید)، و مدتی بعد، آیتم بعدی میرسد (Promise حل میشود).
مفاهیم کلیدی
- تکرارگر ناهمزمان (Async Iterator): شیئی که یک متد
next()
را فراهم میکند که یک Promise را برمیگرداند که به یک شیء با ویژگیهایvalue
وdone
حل میشود، شبیه به یک تکرارگر معمولی.value
نشاندهنده آیتم بعدی در توالی است وdone
نشان میدهد که آیا تکرار کامل شده است یا خیر. - مولد ناهمزمان (Async Generator): نوع خاصی از تابع است که یک تکرارگر ناهمزمان را برمیگرداند. از کلمه کلیدی
yield
برای تولید مقادیر به صورت ناهمزمان استفاده میکند. - حلقه
for await...of
: یک ساختار زبانی که به طور خاص برای تکرار روی تکرارگرهای ناهمزمان طراحی شده است. این ساختار فرآیند مصرف جریانهای داده ناهمزمان را ساده میکند.
ایجاد تکرارگرهای ناهمزمان با مولدهای ناهمزمان
رایجترین راه برای ایجاد تکرارگرهای ناهمزمان از طریق مولدهای ناهمزمان است. یک مولد ناهمزمان تابعی است که با سینتکس async function*
تعریف میشود. در داخل این تابع، میتوانید از کلمه کلیدی yield
برای تولید مقادیر به صورت ناهمزمان استفاده کنید.
مثال: شبیهسازی یک فید داده زنده (Real-time)
بیایید یک مولد ناهمزمان ایجاد کنیم که یک فید داده زنده را شبیهسازی میکند، مانند قیمت سهام یا خوانش سنسورها. ما از setTimeout
برای ایجاد تأخیرهای مصنوعی و شبیهسازی رسیدن دادههای ناهمزمان استفاده خواهیم کرد.
async function* generateDataFeed(count) {
for (let i = 0; i < count; i++) {
await new Promise(resolve => setTimeout(resolve, 500)); // Simulate delay
yield { timestamp: Date.now(), value: Math.random() * 100 };
}
}
در این مثال:
async function* generateDataFeed(count)
یک مولد ناهمزمان را تعریف میکند که یک آرگومانcount
را برای تعیین تعداد نقاط داده برای تولید میپذیرد.- حلقه
for
به تعدادcount
بار تکرار میشود. await new Promise(resolve => setTimeout(resolve, 500))
با استفاده ازsetTimeout
یک تأخیر ۵۰۰ میلیثانیهای ایجاد میکند. این کار ماهیت ناهمزمان رسیدن دادههای زنده را شبیهسازی میکند.yield { timestamp: Date.now(), value: Math.random() * 100 }
یک شیء حاوی مهر زمانی و یک مقدار تصادفی را تولید (yield) میکند. کلمه کلیدیyield
اجرای تابع را متوقف کرده و مقدار را به فراخواننده برمیگرداند.
مصرف تکرارگرهای ناهمزمان با for await...of
برای مصرف یک تکرارگر ناهمزمان، میتوانید از حلقه for await...of
استفاده کنید. این حلقه به طور خودکار ماهیت ناهمزمان تکرارگر را مدیریت میکند و قبل از رفتن به تکرار بعدی، منتظر حل شدن هر Promise میماند.
مثال: پردازش فید داده
بیایید تکرارگر ناهمزمان generateDataFeed
را با استفاده از حلقه for await...of
مصرف کرده و هر نقطه داده را در کنسول ثبت کنیم.
async function processDataFeed() {
for await (const data of generateDataFeed(5)) {
console.log(`Received data: ${JSON.stringify(data)}`);
}
console.log('Data feed processing complete.');
}
processDataFeed();
در این مثال:
async function processDataFeed()
یک تابع ناهمزمان برای مدیریت پردازش داده تعریف میکند.for await (const data of generateDataFeed(5))
روی تکرارگر ناهمزمان بازگشتی ازgenerateDataFeed(5)
تکرار میکند. کلمه کلیدیawait
تضمین میکند که حلقه برای رسیدن هر نقطه داده منتظر بماند.console.log(`Received data: ${JSON.stringify(data)}`)
نقطه داده دریافت شده را در کنسول ثبت میکند.console.log('Data feed processing complete.')
پیامی را مبنی بر اتمام پردازش فید داده ثبت میکند.
مزایای استفاده از تکرارگرهای ناهمزمان
تکرارگرهای ناهمزمان چندین مزیت نسبت به تکنیکهای برنامهنویسی ناهمزمان سنتی مانند callbackها و Promiseها دارند:
- خوانایی بهبود یافته: تکرارگرهای ناهمزمان و حلقه
for await...of
راهی شبیهتر به کد همزمان و قابل فهمتر برای کار با جریانهای داده ناهمزمان فراهم میکنند. - مدیریت خطای سادهتر: شما میتوانید از بلوکهای استاندارد
try...catch
برای مدیریت خطاها در حلقهfor await...of
استفاده کنید که مدیریت خطا را سادهتر میکند. - مدیریت فشار معکوس (Backpressure): از تکرارگرهای ناهمزمان میتوان برای پیادهسازی مکانیزمهای فشار معکوس استفاده کرد، که به مصرفکنندگان اجازه میدهد نرخ تولید داده را کنترل کنند و از اتمام منابع جلوگیری کنند.
- ترکیبپذیری: تکرارگرهای ناهمزمان را میتوان به راحتی با هم ترکیب و زنجیرهای کرد تا پایپلاینهای داده پیچیده ایجاد شود.
- لغو (Cancellation): تکرارگرهای ناهمزمان را میتوان طوری طراحی کرد که از لغو پشتیبانی کنند، که به مصرفکنندگان اجازه میدهد در صورت نیاز فرآیند تکرار را متوقف کنند.
کاربردهای دنیای واقعی
تکرارگرهای ناهمزمان برای انواع کاربردهای دنیای واقعی مناسب هستند، از جمله:
- جریاندهی API: مصرف داده از APIهایی که از پاسخهای جریانی پشتیبانی میکنند (مانند Server-Sent Events، WebSockets).
- پردازش فایل: خواندن فایلهای بزرگ به صورت تکهتکه بدون بارگذاری کل فایل در حافظه. به عنوان مثال، پردازش یک فایل CSV بزرگ به صورت خط به خط.
- فیدهای داده زنده: پردازش جریانهای داده زنده از منابعی مانند بورس اوراق بهادار، پلتفرمهای رسانههای اجتماعی یا دستگاههای اینترنت اشیاء (IoT).
- کوئریهای پایگاه داده: تکرار روی مجموعه نتایج بزرگ از کوئریهای پایگاه داده به صورت کارآمد.
- وظایف پسزمینه: پیادهسازی وظایف پسزمینه طولانیمدت که باید به صورت تکهتکه اجرا شوند.
مثال: خواندن یک فایل بزرگ به صورت تکهتکه
بیایید نشان دهیم چگونه از تکرارگرهای ناهمزمان برای خواندن یک فایل بزرگ به صورت تکهتکه استفاده کنیم و هر تکه را به محض در دسترس قرار گرفتن پردازش کنیم. این روش به ویژه هنگام کار با فایلهایی که برای جا شدن در حافظه بسیار بزرگ هستند، مفید است.
const fs = require('fs');
const readline = require('readline');
async function* readLines(filePath) {
const fileStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
for await (const line of rl) {
yield line;
}
}
async function processFile(filePath) {
for await (const line of readLines(filePath)) {
// Process each line here
console.log(`Line: ${line}`);
}
}
processFile('large_file.txt');
در این مثال:
- ما از ماژولهای
fs
وreadline
برای خواندن فایل به صورت خط به خط استفاده میکنیم. - مولد ناهمزمان
readLines
یکreadline.Interface
برای خواندن جریان فایل ایجاد میکند. - حلقه
for await...of
روی خطوط فایل تکرار میکند و هر خط را به فراخواننده yield میکند. - تابع
processFile
تکرارگر ناهمزمانreadLines
را مصرف کرده و هر خط را پردازش میکند.
این رویکرد به شما امکان میدهد فایلهای بزرگ را بدون بارگذاری کل فایل در حافظه پردازش کنید، که آن را کارآمدتر و مقیاسپذیرتر میکند.
تکنیکهای پیشرفته
مدیریت فشار معکوس (Backpressure)
فشار معکوس مکانیزمی است که به مصرفکنندگان اجازه میدهد به تولیدکنندگان سیگنال دهند که آماده دریافت دادههای بیشتر نیستند. این کار از سرریز شدن مصرفکنندگان توسط تولیدکنندگان و اتمام منابع جلوگیری میکند.
تکرارگرهای ناهمزمان را میتوان برای پیادهسازی فشار معکوس استفاده کرد، به این صورت که به مصرفکنندگان اجازه میدهد نرخ درخواست داده از تکرارگر را کنترل کنند. سپس تولیدکننده میتواند نرخ تولید داده خود را بر اساس درخواستهای مصرفکننده تنظیم کند.
لغو (Cancellation)
لغو، قابلیت متوقف کردن یک عملیات ناهمزمان قبل از اتمام آن است. این میتواند در شرایطی مفید باشد که عملیات دیگر مورد نیاز نیست یا تکمیل آن بیش از حد طول میکشد.
تکرارگرهای ناهمزمان را میتوان طوری طراحی کرد که از لغو پشتیبانی کنند، به این صورت که مکانیزمی برای مصرفکنندگان فراهم شود تا به تکرارگر سیگنال دهند که باید تولید داده را متوقف کند. سپس تکرارگر میتواند منابع خود را پاکسازی کرده و به آرامی خاتمه یابد.
مولدهای ناهمزمان در مقابل برنامهنویسی واکنشی (RxJS)
در حالی که تکرارگرهای ناهمزمان راهی قدرتمند برای مدیریت جریانهای داده ناهمزمان فراهم میکنند، کتابخانههای برنامهنویسی واکنشی مانند RxJS مجموعه ابزار جامعتری برای ساخت برنامههای واکنشی پیچیده ارائه میدهند. RxJS مجموعه غنی از اپراتورها برای تبدیل، فیلتر کردن و ترکیب جریانهای داده، و همچنین قابلیتهای پیشرفته مدیریت خطا و همزمانی را فراهم میکند.
با این حال، تکرارگرهای ناهمزمان یک جایگزین سادهتر و سبکتر برای سناریوهایی ارائه میدهند که به قدرت کامل RxJS نیاز ندارید. آنها همچنین یک ویژگی بومی جاوا اسکریپت هستند، به این معنی که نیازی به افزودن وابستگیهای خارجی به پروژه خود ندارید.
چه زمانی از تکرارگرهای ناهمزمان و چه زمانی از RxJS استفاده کنیم
- از تکرارگرهای ناهمزمان استفاده کنید زمانی که:
- به راهی ساده و سبک برای مدیریت جریانهای داده ناهمزمان نیاز دارید.
- به قدرت کامل برنامهنویسی واکنشی نیاز ندارید.
- میخواهید از افزودن وابستگیهای خارجی به پروژه خود اجتناب کنید.
- نیاز دارید با دادههای ناهمزمان به شیوهای متوالی و کنترلشده کار کنید.
- از RxJS استفاده کنید زمانی که:
- نیاز به ساخت برنامههای واکنشی پیچیده با تبدیلهای داده و مدیریت خطای پیشرفته دارید.
- نیاز به مدیریت همزمانی و عملیات ناهمزمان به شیوهای قوی و مقیاسپذیر دارید.
- به مجموعه غنی از اپراتورها برای دستکاری جریانهای داده نیاز دارید.
- از قبل با مفاهیم برنامهنویسی واکنشی آشنا هستید.
سازگاری مرورگر و پولیفیلها (Polyfills)
تکرارگرهای ناهمزمان و مولدهای ناهمزمان در تمام مرورگرهای مدرن و نسخههای Node.js پشتیبانی میشوند. با این حال، اگر نیاز به پشتیبانی از مرورگرها یا محیطهای قدیمیتر دارید، ممکن است نیاز به استفاده از یک پولیفیل داشته باشید.
چندین پولیفیل برای تکرارگرهای ناهمزمان و مولدهای ناهمزمان موجود است، از جمله:
core-js
: یک کتابخانه پولیفیل جامع که شامل پشتیبانی از تکرارگرهای ناهمزمان و مولدهای ناهمزمان است.regenerator-runtime
: یک پولیفیل برای مولدهای ناهمزمان که به تبدیل Regenerator متکی است.
برای استفاده از یک پولیفیل، معمولاً باید آن را در پروژه خود گنجانده و قبل از استفاده از تکرارگرهای ناهمزمان یا مولدهای ناهمزمان آن را import کنید.
نتیجهگیری
تکرارگرهای ناهمزمان جاوا اسکریپت یک راه حل قدرتمند و زیبا برای مدیریت جریانهای داده ناهمزمان ارائه میدهند. آنها خوانایی بهبود یافته، مدیریت خطای سادهتر و قابلیت پیادهسازی مکانیزمهای فشار معکوس و لغو را ارائه میدهند. چه در حال کار با جریاندهی API، پردازش فایل، فیدهای داده زنده یا کوئریهای پایگاه داده باشید، تکرارگرهای ناهمزمان میتوانند به شما در ساخت برنامههای کارآمدتر و مقیاسپذیرتر کمک کنند.
با درک مفاهیم کلیدی تکرارگرهای ناهمزمان و مولدهای ناهمزمان و با بهرهگیری از حلقه for await...of
، میتوانید قدرت پردازش جریان ناهمزمان را در پروژههای جاوا اسکریپت خود آزاد کنید.
برای مجموعهای از توابع کاربردی برای کار با تکرارگرهای ناهمزمان، کتابخانههایی مانند it-tools
(https://www.npmjs.com/package/it-tools) را بررسی کنید.
برای مطالعه بیشتر
- MDN Web Docs: for await...of
- TC39 Proposal: Async Iteration