Node Master
در حال حاظر NodeJs برای هرکاری 3 API گاهی هم 2 API در دسترس شما قرار میده تا کارتون رو انجام بدید. به مثال زیر دقت کنید. import { readFile as readFileCps, readFileSync } from "node:fs"; import { readFile as readFilePromise } from "node:fs/promises"; // Sync…
یکی از معجزه های #JavaScript ویژگی Closure هست. البته هر زبانی که این ویژگی رو داشته باشه خیلی دست بازی برای پیاده سازی پترن هایی داره که بدون Closure ها شاید ممکن نباشه. خیلی وقت پیش درمورد Async Cps Style صحبت کردیم میتونید برید نگاهی بندازید. نکته ای که میخوایم امروز درموردش صحبت کنیم این که چه جادویی در این تکه کد ساده که به صورت روزانه مینویسیم و کار میکنیم وجود داره ولی خیلی ساده ازش رد میشیم چون کار میکنه! ولی داستان از فقط کار کردن پیچیده تره!
در این کد یک localScope داریم که مستقیما اشاره داره که این variable در localScope مربوط به asyncCPSTask تعریف شده و در حالت عادی وقتی به پایان فانکشن مربوطه میرسیم و از call stack خارج میشیم، localScope هم باید خارج بشه و رسما دیگه نداریمش. ( اگر هم refrence type باشه که GC زحمتش رو باید بکشه )
اگر نکته بالا رو در نظر داشته باشیم باید انتظار داشته باشیم که console.log به ما مقداری رو نشون نده چون به کمک setTimeout یک delay داریم برای اجرا کردن innerFn که قطعا در iteration های بعدی Event-loop اتفاق میافته و صدرصد مطمئن هستیم که تا اون موقع asyncCPSTask از call stack خارج شده. با وجود این موضوع که ما رسما از call stack خارج شدیم و runtime برای ما innerFN رو داخل iteration های بعدی Event-loop اجرا میکنه باز هم مقدار localScope رو میبینیم. دلیل این اتفاق و این رفتار Closure هست.
حالا برای درک بهتر این موضوع دوست دارم دقیقا متن کتاب JavaScript The Definitive Guide - David Flanagan (صفحه 204 بخش 8.6) بیارم:
جمله دوم دقیقا ساده ترین و قشنگ ترین تعریف برای این رفتار هست. به دلیل این که innerFn در زمان تعریف در local scope مربوط به asyncCPSTask قرار داشته میتونه از اون scope استفاده کنه و ما چون در innerFn یک refrence به متغییر localScope داریم رسما asyncCPSTask رو تبدیل کردیم به یک Closure برای innerFn و باتوجه به این که در این حالت مهم نیست فانکشن از کجا call میشه و فقط نکته مهم این هست که کجا تعریف شده فانکشن. حالا چه event-loop برای ما فانکشن رو کال کنه چه خودمون در هر صورت دسترسی به localScope داریم.
خیلی وقت پیش یک نگاهی به این ویژگی تقریبا داشتیم و باهاش decorator pattern رو به صورت کاربردی پیاده سازی کردیم و دیدیم.
https://t.iss.one/NodeMaster/115
function asyncCPSTask(cb) {
const localScope = "localScope";
function innrerFn() {
cb(localScope);
}
setTimeout(innrerFn, 1000);
}
asyncCPSTask((data) => console.log(data));در این کد یک localScope داریم که مستقیما اشاره داره که این variable در localScope مربوط به asyncCPSTask تعریف شده و در حالت عادی وقتی به پایان فانکشن مربوطه میرسیم و از call stack خارج میشیم، localScope هم باید خارج بشه و رسما دیگه نداریمش. ( اگر هم refrence type باشه که GC زحمتش رو باید بکشه )
اگر نکته بالا رو در نظر داشته باشیم باید انتظار داشته باشیم که console.log به ما مقداری رو نشون نده چون به کمک setTimeout یک delay داریم برای اجرا کردن innerFn که قطعا در iteration های بعدی Event-loop اتفاق میافته و صدرصد مطمئن هستیم که تا اون موقع asyncCPSTask از call stack خارج شده. با وجود این موضوع که ما رسما از call stack خارج شدیم و runtime برای ما innerFN رو داخل iteration های بعدی Event-loop اجرا میکنه باز هم مقدار localScope رو میبینیم. دلیل این اتفاق و این رفتار Closure هست.
حالا برای درک بهتر این موضوع دوست دارم دقیقا متن کتاب JavaScript The Definitive Guide - David Flanagan (صفحه 204 بخش 8.6) بیارم:
Like most modern programming languages, JavaScript uses lexical scoping. This
means that functions are executed using the variable scope that was in effect when
they were defined, not the variable scope that is in effect when they are invoked
جمله دوم دقیقا ساده ترین و قشنگ ترین تعریف برای این رفتار هست. به دلیل این که innerFn در زمان تعریف در local scope مربوط به asyncCPSTask قرار داشته میتونه از اون scope استفاده کنه و ما چون در innerFn یک refrence به متغییر localScope داریم رسما asyncCPSTask رو تبدیل کردیم به یک Closure برای innerFn و باتوجه به این که در این حالت مهم نیست فانکشن از کجا call میشه و فقط نکته مهم این هست که کجا تعریف شده فانکشن. حالا چه event-loop برای ما فانکشن رو کال کنه چه خودمون در هر صورت دسترسی به localScope داریم.
خیلی وقت پیش یک نگاهی به این ویژگی تقریبا داشتیم و باهاش decorator pattern رو به صورت کاربردی پیاده سازی کردیم و دیدیم.
https://t.iss.one/NodeMaster/115
👍10
ما Developer ها به Magic عادت داریم. بیشتر وقت ها کدی که مینویسیم فقط میخوایم کار کنه. اما چطور کار کردن اون کد خیلی اهمیتی نداره تا وقتی که کدمون کار میکنه. حالا چه به صورت جادویی کار کنه چه با جزئیات کامل بدونیم پشت پرده چخبر هست. گاها بعضی از این جادو ها اینقدر پیچیده و ترسناک به نظر میان که اصلا بهشون نزدیک نمیشیم. یکی از این Magic ها Promise ها در #JavaScript هست. اکثرا فکر میکنیم یک چیزی هست که داخل گوشت و وجود #JavaScript فرو رفته و به هیچ عنوان نمیشه تغییرش داد یا اگر هم بشه خیلی سخت هست. کاری به پیاده سازی اصلیش امروز نداریم. امروز بعد از این مقدمه طولانی میخواهیم باهم یاد بگیرم چطور یک Object بسازیم که شبیه به Promise عمل میکنه. یعنی میتونیم await کنیم اون رو با وجود این که اصلا اون Promise نیست و یک Object ساده هست. به این کد پایین دقت کنید.
معمولا اولین واکنش افراد به این تکه کد "WTF" هست. داستان از این قرار هست که خیلی قبل تر از این که Promise ها به صورت استاندارد وارد #JavaScript بشن پیاده سازی های مختلف ازش وجود داشته و هرکدوم implemention detail خاص خودشون رو داشتن ولی طی گذر زمان بلاخره رسیدن به این interface که الان داریم هر روز باهاش کار میکنیم ( تقریبا شبیه به وضعیت decorator ها و پروپزوال مربوط بهش ). بزارید یکم بیشتر وارد داستان بشیم.
برای این که بتونیم یک object رو await کنیم لزوما نیازی نداره حتما یک instance از Promise باشد. بلکه هر object که Promise-like باشد میتواند await بشود. این تعریف دقیقا تعریف Duck-typing هست که قبلا مفصل درموردش حرف زدیم. حالا سوال پیش میاد که Promise-like یعنی چی؟
- هر class که thanable interface را implement کند میتوان آن را await کرد.
پس میتونیم نتیجه بگیریم که همه Promise ها thenable هستند ولی همه thenable ها Promise نیستند.
حالا میرسیم که thanable interface چیست؟ در ساده ترین حالت دقیقا مثال بالا رو در نظر بگیرد. یک object یا class که then method رو که با دوتا argument که هردو callback هستن یکی برای وقتی که reject شده و یکی برای وقتی resolve میشه invoke میشود و ما میتونیم با اون دوتا رفتاری که Promise-like هست رو از خودمون نشون بدیم.
حالا این ویژگی به ما اجازه داده که پکیج های معروفی مثل promise-retry ببینیم یا حتی یکم تلاش کنیم به کمک Promise-like ها در ES5 بتونیم چیزی شبیه بهش رو داشته باشیم که البته این موضوع خودش یک بحث دیگس اگر دوست دارید نگاهی به promise-polyfill یا babel-polyfill بندازید.
این هم پست مربوط به duck typing به نظرم نگاهی بندازید:
https://t.iss.one/NodeMaster/128
const thenable = {
then(resolve, reject) {
setTimeout(() => resolve("Hello from thenable!"), 1000)
},
}
const txt = await thenable
console.log(txt)معمولا اولین واکنش افراد به این تکه کد "WTF" هست. داستان از این قرار هست که خیلی قبل تر از این که Promise ها به صورت استاندارد وارد #JavaScript بشن پیاده سازی های مختلف ازش وجود داشته و هرکدوم implemention detail خاص خودشون رو داشتن ولی طی گذر زمان بلاخره رسیدن به این interface که الان داریم هر روز باهاش کار میکنیم ( تقریبا شبیه به وضعیت decorator ها و پروپزوال مربوط بهش ). بزارید یکم بیشتر وارد داستان بشیم.
برای این که بتونیم یک object رو await کنیم لزوما نیازی نداره حتما یک instance از Promise باشد. بلکه هر object که Promise-like باشد میتواند await بشود. این تعریف دقیقا تعریف Duck-typing هست که قبلا مفصل درموردش حرف زدیم. حالا سوال پیش میاد که Promise-like یعنی چی؟
- هر class که thanable interface را implement کند میتوان آن را await کرد.
پس میتونیم نتیجه بگیریم که همه Promise ها thenable هستند ولی همه thenable ها Promise نیستند.
حالا میرسیم که thanable interface چیست؟ در ساده ترین حالت دقیقا مثال بالا رو در نظر بگیرد. یک object یا class که then method رو که با دوتا argument که هردو callback هستن یکی برای وقتی که reject شده و یکی برای وقتی resolve میشه invoke میشود و ما میتونیم با اون دوتا رفتاری که Promise-like هست رو از خودمون نشون بدیم.
حالا این ویژگی به ما اجازه داده که پکیج های معروفی مثل promise-retry ببینیم یا حتی یکم تلاش کنیم به کمک Promise-like ها در ES5 بتونیم چیزی شبیه بهش رو داشته باشیم که البته این موضوع خودش یک بحث دیگس اگر دوست دارید نگاهی به promise-polyfill یا babel-polyfill بندازید.
این هم پست مربوط به duck typing به نظرم نگاهی بندازید:
https://t.iss.one/NodeMaster/128
Telegram
Node Master
امروز درمورد Duck Typing صحبت میکنیم. این موضوع مربوط به رفتار زبان های dynamic type میباشد. البته این تکنیک در زبان هایی static type هم در حالت های خاصی استفاده میشود. برای درک این موضوع با مثال در #Python شروع میکنیم.
class Duck:
def fly(self):
…
class Duck:
def fly(self):
…
👍11