در این مقاله به بررسی دو میان افزار thunk و saga و مقایسه این دو می پردازیم. در پایان نیز میان افزار محبوب خود را معرفی خواهیم کرد.
Thunk چیست؟
Thunk یک تابع است که مانند یک wrapper عمل می کند. در واقع یک عبارت را برای به تاخیر انداختن دریافت value ، wrap می کند. برای مثال در کد زیر تابع foo مانند یک thunk برای به تاخیر انداختن نتیجه محاسبات عمل می کند.
در redux ، thunk به شما اجازه می دهد تا action creator هایی را بنویسید که به جای یک action object معمولی یک تابع را بر می گرداند. Thunk می تواند برای ایجاد تاخیر ارسال یک action تا تکمیل یک خط از کدهای asynchronous مورد استفاده قرار گیرد.
در زیر طرح کلی پردازش مرحله به مرحله thunk در Redux را مشاهده می فرمایید:
1- بررسی می کند که Action ورودی چیست.
اگر Action یک action object معمولی باشد thunk هیچ کاری انجام نمی دهد و action object توسط store reducer پردازش می شود.
2- اگر action یک تابع باشد، thunk آن را فراخوانی می کند و به آن store’s dispatch و تمامی state ها و متد ها و آرگومان ها را ارسال می کند.
3- بعد از اینکه تابع run شد، thunk ، action ایی که State را بروز می کند ارسال می کند. مطابق زیر:
بنابراین به صورت خلاصه thunk دارای دو بخش است:
1- یک thunk creator که در واقع یک action creator است که یک thunk را بر می گرداند.
2- خود thunk که یک تابع است که توسط thunk creator برگردانده می شود و dispatch و setState را به عنوان آرگومان قبول می کند.
دلیل اینکه ما نیاز داریم از یک middleware مانند thunk استفاده کنیم این است که Redux store تنها از جریان داده ای همزمان (synchronous) پشتیبانی می کند بنابراین میان افزار یک راه نجات برای زمانی است که دیتا لحظه ای بر نمی گردد و باید برای دریافت اطلاعات منتظر بمانیم. در واقع میان افزار جریان داده ای غیرمتقارن (asynchronous) را پشتیبانی می کند. thunk هرچیزی که شما ارسال کنید را تفسیر می کند و در نهایت یک plain object را بر می گرداند که اجازه می دهد جریان داده ای متقارن redux (synchronous data flow) از سر گرفته شود. بنابراین این میان افزار می تواند بسیاری از نیازهای ارسال نامتقارن داده را برطرف کند.
درتصویر زیر نمودار دانلود میان افزارهای Redux را مشاهده می نمایید. همانطور که دیده می شود علاوه بر thunk میان افزارهای محبوب دیگری نیز وجود دارد که از بین آن ها نام saga معروف تر از بقیه می باشد(تعداد star).
Saga چیست؟
Saga یک کتابخانه است که هدف آن کنترل کردن آسان تر side-effect های نرم افزار(Action های نامتقارن مانند fetch data) و اجرای موثر تر آن می باشد.
ایده این هست که saga شبیه یک thread در برنامه شما می باشد که فقط وظیفه پاسخگویی به side-effect ها را برعهده دارد. اگرچه برخلاف thunk که یک تابع callback را برای کنترل به کار می گیرد، saga چون یک thread است می تواند به وسیله برنامه اصلی با یک action نرمال Redux شروع شود، pause شود و cancel شود.
مانند thunk ، saga نیز به state های برنامه ی Redux دسترسی کامل دارد و می تواند Action های redux را به خوبی ارسال کند. به منظور کنترل state ها، saga یک ویژگی جدید ES6 به نام gnerators را مورد استفاده قرار می دهد.
Generator ها توابعی هستند که می توانند exit شوند و دوباره وارد شوند.
شما می توانید به سادگی یک تابع generator را صدا بزنید، به این صورت که بعد از کلمه function یک * قرار دهید، این امر منجر می گردد تا تابع به سرعت excute نشود و به جای آن یک object تکرار شونده برگردانده شود.
زمانی که متد next() صدا زده می شود تابع generator تا اولین yield اجرا می شود. متد next() یک object با یک value که مقدار ارسالی است و یک Boolean به معنی done را بر می گرداند، که نشان می دهد generator در کجای کد آخرین value خود را واگذار کرده است.
اما اجازه بدهید یک قدم به عقب برویم. Yield چیست؟
خط دوم قطعه کد بالا یک عبارت yield را فراخوانی می کند. زیرا وقتی که شما generator را restart می کنید یک value جدید را ارسال خواهید کرد و مقدار جدید به عنوان یک value جدید ارزشیابی خواهد شد.
در واقع ماهیت yield این است که یک درخواست برای value را ارسال می کند.
برخلاف توابع معمولی جاوا اسکریپت، توابع generator تا پایان اجرا می شوند. توابع generator می توانند به صورت اشتراکی باشند. به این صورت که generator می تواند انتخاب کند زمانی که یک تابع اختلال(intrupt) ایجاد می کند، در قسمت دیگری از کد همکاری داشته باشد.
شما می توانید از کلیدواژه yield برای pause کردن تابع از درون خودش استفاده کنید.توجه داشته باشید که هیچ چیز نمی تواند generator را از بیرون pause نماید. اگرچه این نکته نیز وجود دارد که در صورتی که تابع generator از درون pause شود نمی تواند خودش را restart کند و تنها یک کنترل خارجی می تواند تابع generator را restart نماید.
خب با تفاسیر بالا برنده کیست؟ saga یا thunk؟
تصاویر زیر شامل قطعه کدهایی است که با هر دو میان افزار نوشته شده است.
تصویر اول thunk و تصویر دوم saga می باشد.
مزیت استفاده از saga در مقایسه با thunk
1- عدم وجود callbackها
شما می توانید از callback های جهنمی رهایی یابید. به این معنی که شما می توانید از pass شدن ها جلوگیری کنید و درون کد خودتان قسمت هایی که می خواهید را فراخوانی کنید.
2- تست آسان Data flow
علاوه بر این شما می توانید جریان داده ای نامتقارن خود را به آسانی تست کنید. فراخوانی و قرار دادن method ها ، object های جاوااسکریپت را بر می گرداند.بنابراین شما می توانید به سادگی هر value که به وسیله تابع saga ، yield می شود را با یک عبارت مقایسه ای تست کنید.
به عبارت دیگر thunk ، primise هایی را بر می گرداند که بررسی آن ها بسیار دشوار است.تست نمودن thunk اغلب پیچیدگی های درخواست api ها؛ axios request ها یا توابع دیگر را دارد. در صورتی که در saga به هیچ یک از این ها نیاز ندارید.
3- توانایی کنترل act با توجه به Action ها
شما می توانید با توجه به Action مورد نظر خود برنامه را pause, start یا cancel نمایید.
Thunk اگرچه برای پروژه های کوچک و مبتدیان به دلیل اینکه منطق پیاده سازی اش ساده است و به دلیل اینکه نیاز به توابع جدید (generators) و keyword های جدید ندارد بسیار خوب است اما در پروژه های بزرگ و پیچیده به دلیل پیچیدگی که در callback ها و تست ها ایجاد می کند مطلوب نمی باشد.
نتیجه گیری:
با توجه به اینکه هر دو ابزار معرفی شده middleware هایی برای redux هستند تا جریان داده ای نامتقارن را کنترل نمایند استفاده از هر دو امکان پذیر بوده و انتخاب یکی از آن ها تنها به Scale پروژه شما بستگی دارد.
منابع:
https://medium.com/@shoshanarosenfield/redux-thunk-vs-redux-saga-93fe82878b2d
https://decembersoft.com/posts/redux-thunk-vs-redux-saga/