Node Master
1.02K subscribers
24 photos
2 files
156 links
Group Chat: @nodemastergp
Admin: @napoleon_n1
Download Telegram
Node Master
رشته پست مربوطه :‌ https://t.iss.one/NodeMaster/81 خب بریم سراغ باگی که قرار بود راجع بهش صحبت کنیم. بزارید یکم راجع تاریخچه باحال این باگ صحبت کنیم. Null References: The Billion Dollar Mistake اولین بار این null رو در زبان ALGOL در سال 1965 درست کرد فقط به دلیل…
در پست قبلی راجع به اصل باگ توضیحاتی دادیم ولی رسیدم سروقت این که چرا ما NaN میگیریم. بزارید با یک مثال در #python و #javascript رو کنار هم بزاریم و اونجا میتونیم بهتر متوجه بشیم.
>>> 2 + "text"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

> 2 + "text"
'2text'

این دو خط خیلی شبیه به هم هستند هردو یک مقدار string رو با یک مقدار integer جمع کردیم ولی جواب نهایی خیلی متفاوت هست. دقیقا همین رفتار باعث میشه که زبان ها رو به دو دسته تقسیم کنیم
- weakly typed
- strong type
قبل از این که توضیح بدم راجع به این دو این رو در نظر بگیرد که #python یک زبان strong type و #javascript یک زبان weak type هست و حالا با دونستن این فکت میتونیم توضیحات رو کامل تر کنیم.
به عنوان مثل در زبان های strong type اگر دو expresion از type های مختلف بخواد پردازشی روشون انجام بشه خود compiler یا interpreter جلوگیری میکنه از این کار مثل کد پایتون بالا که به صراحت میگه هردو باید یک type باشند و فرایند این تبدیل باید به صورت explicit توسط برنامه نویس انجام شود ولی در زبان های weakly type نتیجه میشه تکه کد js که این به نوعی خیلی میتونه ترسناک باشه! حالا اگر بخوایم نتیجه شبیه به کد js داشته باشیم منظور از explicit بودن در مثال پایین میبینید.
>>> str(2) + "text"
'2text

اینجا میبینید که برنامه نویس از فانکشن str برای تبدیل integer به string به صراحت ( explicitly ) استفاده کرده و منظور از explicit بودن این هست.
تفاوت زبان های strong و weak type به همینجا خطم نمیشه و ساعت ها میشه راجع بهش صحبت کرد ولی خب در همین حد فعلا کافی هست چون موضوعات دیگه ای هم هست که باید اشاره کنیم.

بزارید من اینجا یکبار دیگ تاکید کنم که "زبان های Weakly type میتوانند خیلی ترسناک باشند." بزارید این رو یکم بازتر کنم براتون با مثال
> null + 2
2
> undefined + 2
NaN

>>> None + 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

داخل سوالی که بالا طرح کرده بودم دقیقا امکان داشت این اتفاق تک کد بالا پیش بیاد که مقدار undefined با 2 جمع بشه و اینجا javascript هیچ اروری به شما نمیده و برنامه روند عادی خودش رو طی میکنه و خدا فقط میدونه کی و کجا یک validation از این جلوگیری کنه. حتی امکان داره ماه ها این باگ ها داخل کدتون بمونه و شما خبر نداشته باشید و از اون بدتر امکان ایجاد record های خراب داخل database هم هست. نکته جالب اینجا بود که وقتی integer رو با null جمع کردم و عدد 2 رو گرفتم خودم سوپرایز شدم. کلا weakly type ها همیشه میتونن شما رو سوپرایز کنن.
ولی خب به عنوان مثال در زبان strong type به صراحت interperter گیر میده و حتی ممکنه برنامه crash کنه.

این نکته خیلی مهم رو توجه کنید که این به معنی بد بودن weakly type lang ها یا خوب بودن strong type lang ها نیست هرکدوم مزایا و معایب خودشون رو دارن و این ما هستیم که باید از اینها درست استفاده کنیم. فقط باید این رو درنظر بگیرید که در زبان ها weakly type باید خیلی خیلی دقت بیشتری کنیم.

نظر شخصی :‌
- در اینجور مواقع من ترجیح میدم برنامه crash کنه تا در سکوت به کار خودش ادامه بده چون crash کردن خیلی خسارت کمتری وارد میکنه تا دیتای خراب روی دیتابیس

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

فکر میکردین پشت پرده یک NaN گرفتن اینقدر داستان باشه؟ کامنت کنید😂
#Tip
👍8
در خیلی از زبان های برنامه نویسی مثل #Python #javascript #Golang و ... میبینیم که میگن فانکشن ها first class citizen function هستن. این موضوع خیلی میتونه تاثیر در برنامه های نوشته شده در زبان مورد نظر داشته باشه.
اصلا چرا بهشون میگیم first class citizen ؟
چیکار ها میتونیم انجام بدیم باهاشون؟

یکم راجع به این موضوع فکر کنید فردا راجع بهش صحبت خواهیم کرد.
https://developer.mozilla.org/en-US/docs/Glossary/First-class_Function
👍4
امروز درمورد Duck Typing صحبت میکنیم. این موضوع مربوط به رفتار زبان های dynamic type میباشد. البته این تکنیک در زبان هایی static type هم در حالت های خاصی استفاده میشود. برای درک این موضوع با مثال در #Python شروع میکنیم.
class Duck:
def fly(self):
print("quak quak !")

class Airplane:
def fly(self):
print("boom boom !")

#Duck Test
def fly_test(vehicle):
vehicle.fly()

flyable = [Duck() , Airplane()]
for f in flyable:
fly_test(f)

یک جمله خیلی معروف و کلیدی در این مورد وجود داره که باتوجه به اون مثال بالا رو توضیح میدیم."if it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck."
به این معنی هست "اگر شبیه به اردک شنا میکنه کواک میکنه پس احتملا اردک هست."
در زبان های dynamic type مفهومی به اسم interface نداریم و اگر یک class یک method رو تغریف کرده باشه شما بدون نیاز به type میتونید اون method رو به راحتی call کنید. الان در کد بالا هیچ نشانه ای از type نیست و فانکشن fly_test بدون داشتن دانشی از این که ایا arg ورودی fly method دارد یا خیر این متد رو call میکنه. به این کار میگن duck testing. در کل بگم به هیچ عنوان fly_test براش مهم نیست vehicle چی هست فقط براش مهم هست که fly وجود داشته باشد.
حالا سوال پیش میاد که اگر این متد نبود چی؟ الان در این مثال اگر اگر متد fly نباشه روی یکی از این کلاس ها خب runtime error داریم و برنامه crash میکنه. دو دیدگاه برای جلوگیری از این مدل runtime error ها داریم که در پست بعدی برسی میکنیم هردو رو.
نکته جالب دیگه این هست که Airplane و Duck این دو کلاس هیچ رابطه ای با هم ندارند و صرفا فقط به صورت قراردادی هردو یک fly method رو implement کردن. در #TypeScript از Duck Testing زیاد استفاده میشه برای داشتن کد TypeSafe در حقیقت تکنیک Type Predict به نوعی Duck Test میتونه محسوب بشه.

این مقدمه ای برای این بحث بود. در حقیقت برای داشتن همچین مدل رفتاری مدل های فکری مختلفی در زبان های مختلف هست. در زبان #Java ما از inerface ها و abstract class ها استفاده میکنیم در #GoLang شما یک نوع دیگه از Duck Typing رو میبینید که بهش میگن Structural Typing و interface های golang بهمون کمک میکنن در #TypeScript هم Structural Typing به شدت مرسوم هست و مثال خواهیم زد، در پایتون که Duck Typing رو دیدید ولی نکته جالب اینجا هست که Structural Typing و abstract class ها هم در پایتون خیلی بهمون کمک میکنن ولی خب کمتر کسی از وجود این ها خبر داره.
این نکته هم بگم که هیچ کدوم از این راه حل ها نسبت به دیگری برتری خاصی ندارند صرفا نوع تفکره بیشتر البته البته زبان های Static و TypeSafe میتونن develop رو راحت تر کنن در این مورد. این که کدوم تکنیک در کدوم زبان استفاده میشه مهم هست چون که به عنوان مثال Duck Typing در java خیلی مرسوم نیست و استفاده ازش سخت تره اما در یک حالت های خاصی خیلی میتونه در این زبان کمک کنه.
#Tip
👍11
Node Master
امروز درمورد Duck Typing صحبت میکنیم. این موضوع مربوط به رفتار زبان های dynamic type میباشد. البته این تکنیک در زبان هایی static type هم در حالت های خاصی استفاده میشود. برای درک این موضوع با مثال در #Python شروع میکنیم. class Duck: def fly(self): …
قبلا درمورد Type Predict ها در #TypeScript صحبت کردیم از این لینک یک مطالعه کنید

https://t.iss.one/NodeMaster/90

و این که یک قسمت گفتیم که دو دیدگاه برای فیکس کردن همچین crash هایی در Runtime وجود دارد.

- Easier to ask for forgiveness than permission (EAFP)
- Look before you leap (LBYL)
قبل از این که درمورد این دو دیدگاه صحبت کنیم باید بگم که هرکدوم از این ها در یک زبان هایی مرسوم هست. به عنوان مثال در #Python نماد کد تمیز استفاده از EAFP و در #Javascript باید از LBYL استفاده کرد.
خودتون یکم برید راجع به این دوتا مطالعه کنید و بنظرتون بگید که Type Predict ها در #TypeScript از کدوم از این استایل ها استفاده میکنن؟
👍1
همیشه دنبال یک معادل مناسب برای #Python Beautiful Soup در اکوسیستم #NodeJS بودم که نخوام با headless browser ها scrap انجام بدم. امروز این پکیج رو پیدا کردم شما هم اگر دوست داشتید یک چک کنید.
https://www.npmjs.com/package/cheerio
یکی از نقاط ضعفی که در #JavaScript وجود داره نبود استاندارد Error های مناسب طبق یک class hierarchy خوب مثل #Java یا #Python هست. حالا در #NodeJS سعی بر این شده که به این وضعیت رسیدگی بشه و وضعیت این Error ها به نسبت خود vanilla js خیلی بهتر هست.
در جهت همین موضوع یکی از Error های خیلی مهم در #NodeJS در حقیقت SystemError ها هستن. اگر دوست دارید درمورد سیستم عامل بیشتر بدونید این Error class شما رو یک خورده به سیستم عامل نزدیک تر میکنه. حالا سوال پیش میاد که SystemError چیست؟ اگر برنامه شما درخواستی داشته باشه از سیستم عامل ولی به هر دلیلی محدودیت هایی وجود داشته باشه #NodeJS با SystemError به شما اطلاع میده.
import fs from "node:fs/promises";
import util from "node:util";
try {
await fs.readFile("./invalid-file.txt");
} catch (err) {
console.log(err);
console.log(util.getSystemErrorName(err.errno));
}

به این کد دقت کنید. ما در اینجا سعی میکنیم که یک فایلی رو از روی دیسک بخونیم که اصلا این فایل وجود نداره. در حقیقت داریم از سیستم عامل درخواست open syscall داریم که فایل رو بخونه یا ایجاد کنه ( در صورت استفاده از flag مناسب برای syscall که اینجا ست نکردیم) با توجه به این مثال به به محدودیت از سمت سیستم عامل میخوریم. و حاصل این کد یک SystemError هست. حالا در "node:util" یک فانکشن کمکی وجود داره که درصورتی که کد SystemError رو که در err.errorno میتونید بهش دسترسی داشته باشید بهش بدید. اسم که در اینجا ENOENT به من میده. این SystemError ها چون مربوط به سیستم عامل هست به زبان برنامه نویسی خاصی وابستگی ندارند و در همه زبان ها شما میتونید اینها رو ببینید.
#Tip
👍13
اگر تا حالا فکر کردین که چطور فریمورک ها رو برای ما developer های عادی توسعه میدن جواب شما یک magic هست به اسم #Metaprogramming . این کار مثل برنامه نویسی معمولی که ما انجام میدیم تکنیک های مختلف داره که یکی از اونها رو در این پست باهم برسی کردیم.
https://t.iss.one/NodeMaster/115

البته لازم به ذکر هست یکسری Pattern به صورت کلی وجود داره که بین زبان های مختلف مشترک هست و به نوعی پیاده سازی های مختلف ازش دیده میشه و البته یکسری زبان ها هم یکسری تکنیک های مخصوص به خودشون رو هم دارن که اون ها رو خاص تر میکنه.

حالا یکی از این جادو های کاربردی #Metaprogramming این هست که اگر یک Instance از یک Object داریم که یک Parrent Class داره چطور فقط و فقط متوجه بشیم که چه Attr هایی مخصوص به اون Child Class هست و Attr های Parrent رو نادیده بگیریم. و جواب این سوال خیلی سادس :
const myPrototype = { x: 1, y: 2 }; // Parrent
const myObj /* Child */ = Object.create(myPrototype);

myObj.name = "point"; // Instance Prop

console.log(Object.getOwnPropertyNames(myObj)); // [ 'name' ]
console.log(Reflect.ownKeys(myObj)); // [ 'name' ]
console.log("x in myObj :", "x" in myObj);

در #JavaScript با استفاده از Object.getOwnPropertyNames و Reflect.ownKeys(myObj) میتونیم متوجه بشیم که کدام Atter ها مربوط به Instnace ما یعنی myObj هستند. به این خاطر هست که Atter های Parrent یعنی x و y رو کامل نادیده گرفته میشه و فقط در name رو ما به عنوان Attr میبینم که مستقیما روی Child Instance ما تعریف شده و نه روی Parrent. شاید این مثال یکم پیچیده باشه بخاطر ماهیت Object ها در #JavaScript هست. همین رو اگر بخوایم در #Python برسی کنیم شاید یکم قابل درک تر باشه و البته مثال های دیگ هم در #JavaScript میشه زد ولی خب به نظرم بهتره بزاریم برای کنجکاوی خودتون.
class Parrent:
def __init__(self) -> None:
self.x = 1
self.y = 2

class Child(Parrent):
def __init__(self) -> None:
super().__init__()
self.name = "point"


myObj = Child()

parrentAttrs = set(dir(Parrent()))
childAtters = set(dir(Child()))

print(childAtters.difference(parrentAttrs))

یک نکته خیلی جذاب کلا خارج از بحث که به نظرم ارزش توجه کردن داره استفاده از set برای رسیدن به نتیجه ای شبیه به مثال #JavaScript هست. و اینجا به عنوان مثال مستقیم یکی از کاربرد های Set Object و Set theory رو مستقیم و خیلی کوچیک میبینید.
نکته ای که خیلی مهم باید توجه بشه این هست که بین تکنیک استفاده از ownKeys و getOwnPropertyNames تفاوت هایی وجود داره که اگر استقبال بشه عمیق تر وارد این موضوع میشیم.

#Tip
👍8
من شخصا به عنوان یک #Python Hater دو آتیشه هستم.
ولی یک چیزی داره و عجیب خوب هست. حداقل من تا حالا شبیهش رو داخل هیچ اکوسیستم دیگه ای ندیدم و اگر شبیهش رو میشناسید معرفی کنید خوشحال میشم.

این ویژگی خیلی واقعا خفن pickle std برای #Python هست. در حقیقت pickle یک serialization format مثل #JSON هست و مثل #Protobuf یک فرمت binaray هست. حالا سوال پیش میاد چه نکته ای داره.
شما به pickel هر object در اکوسیستم #Python رو میتونید serialize کنید. هر Object با تمام پیچدگی ها custom که داره. بزارید با مثال توضیح بدم.
تصور کنید یک JSON دارید و اون رو میخواید map کنید به یک class و خب معمولا با Factory ها و به شکل های مختلف این اتفاق میافته.
class User {
constructor(name) {
this.name = name;
}
static FromJsonString(input) {
const result = JSON.parse(input);
return new User(result.name);
}
}

const runTimeInput = '{"name":"imanhpr"}';
const user = User.FromJsonString(runTimeInput);

console.log(user instanceof User);
console.log(user.name);


همه چی خیلی منطقی کار میکنه در این سناریو. نکته جالب که اینجا و توجه بهش جالبه این هست که در اینجا به صورت ذاتی هیچ metadata وجود نداره که مشخص بشه مقدار runTimeInput واقعا از جنس User هست و معمولا با استفاده از validation های مختلف و factory متد ها باید مشخص کنیم. اگر بخوایم یک سناریو دیگ رو برسی کنیم که یکم پیچیده تر باشه. شما به هردلیل منطقی یا غیرمنطقی تصمیم داری یک function که در نهایت یک object هست رو serialize کنید. چطور این کار میکنید؟
function hello() {
console.log("hooo");
}
console.log(JSON.stringify(hello));

باتوجه به این که در #JSON به طور کلی هیچ طوره فرمت استانداردی وجود نداره برای این کار منطقی هست کد بالا کار نکنه. ( البته با trick همین کد بالا رو میتونید کاری کنید که بشه ولی کاری به اون سناریو نداریم :) )
یک سناریو پیچیده تر اینه که شما یک object داشته باشید که property descriptor های اون رو دست کاری کردین چطور میخواین بدون ست کردن flag یا نشانه در هنگام serialize کردن و بعد از دریافت همون object متوجه بشید که property descriptor رو چطور باید ست کنید.

کلا بخوام خلاصه و جمع بندی کنم هرچیزی که مختص object هست که در runtime وجود داره مثل انواع reflection ها و property descriptor و method ها و ... .
اینجا هست که pickel خودش رو نشون میده.
import pickle

class NumberOne:
def __get__(self, obj, objtype=None) :
return 1


class User:
__name = "iman"
one = NumberOne()

@property
def name(self):
print("hello from getter")
return self.__name

def __eq__(self, value: object) -> bool:
if isinstance(value , User) and self.__name == value.__name:
return True
return False

def __str__(self) -> str:
return f'User(name={self.__name})'

def hello():
print("hello from function")

# instance sterilization
user_instance_bin = pickle.dumps(User())
run_time_user = pickle.loads(user_instance_bin)
print("user instanceof User :",isinstance(run_time_user , User))
print("user object :" , run_time_user)
print("equality check :" , run_time_user == User())
print("getter invocation :" , run_time_user.name)

# function sterilization
function_bin = pickle.dumps(hello)
run_timefn = pickle.loads(function_bin)
run_timefn()

# class sterilization
cls_bin = pickle.dumps(User)
runtime_cls = pickle.loads(cls_bin)
print("class check:" , issubclass(runtime_cls , User))

# << Exciting part >>
print("WTF:",run_time_user.one)

در اینجا شما هر جسم قابل لمسی رو در #Python میتونید serialize کنید و مستقیم با خوندن همون data یک object قابل لمس در runtime با تمام metadata ها دریافت کنید. از class گرفته تا یک gatter method که با decorator هست تا انواع magic method ها یا به قول #Pythonic ها dunder method.
از همه جالب تر اون بخش آخر هست که یک Python Descriptor رو هم کنار object های User گذاشتیم و میبینیم اون هم به صورت کامل میبینیم. اگر بخوام خلاصه در یک جمله توضیح بدم رسما با اینکار انگار از یک object دارید فایل exe میسازید به جای یک برنامه بزرگ!

نکته بعدی این که این pickel استاندارد مربوط به کد های python هست و جای دیگ تقریبا میشه گفت کاربردی نداره.

https://docs.python.org/3/library/pickle.html
👍5
یکی از نکات زیبا که درمورد #Deno و #JSR که وجود داره این هست که شما میتونید از پکیج های std دینو در #NodeJS استفاده کنید. همشون نه ولی به اکثرشون دسترسی دارید. به لینک زیر یک نگاهی بندازید و مشخصا میبینید که کدوم پکیج ها رو میتونید در پروژه #NodeJS خودتون استفاده کنید.
https://jsr.io/@std
به عنوان مثال پکیج @std/msgpack به پروژه خودمون اضافه میکنیم.
npx jsr add @std/msgpack

و بعد کد زیر رو با #NodeJS اجرا کنید.
import * as msgpack from "@std/msgpack";

const data = { name: "node-master" };

const enc = msgpack.encode(data);
const dec = msgpack.decode(enc);
console.log(dec);

حالا سوال پیش میاد که اصلا چرا همچین کاری باید کنیم. باتوجه به این که #JavaScript خودش std بزرگی مثل #Python یا #Java نداره گاهی اوقات نیاز به یک سری کدهای کمکی برای پروژه خودتون دارید و پکیج هایی مثل Lodash, es-toolkit کمک بزرگی کردن. اما حالا #Deno هم با این کار به اکوسیستم کمک بزرگی کرده و یک رنج از ابزار های کمکی که ممکنه در loadash و بقیه شبیهه بهش پیدا نکنید رو شاید اینجا پیدا کنید.

به عنوان مثال اگر پروژه کوچیکی دارید که فقط یک instance از server شما وجود داره استفاده از ابزاری مثل #Redis صرفا برای Cache زیاد منطقی نیست و خب در اکثرا اوقات یک Map ساده مشکل شما رو میتونه حل کنه. اگر ویژگی TTL هم بخواید داشته باشید خودتون میتونید پیاده سازی کنید با کمک Map ولی معمولا از پکیج node-cache در این سناریو استفاده میکنن. حالا یک گزینه دیگ داریم که مربوط به #Deno std هست.
https://jsr.io/@std/cache
این پکیج جدا از مثال بالا مدل های مختلف دیگ از cache رو در اختیار شما میزاره که نگاهی بندازید و جالبه.
👍5
درود دوستان ارادتتتت.
خیلی وقت بود میخواستم این ویدیو رو بگیرم. روزی که من #Python گذاشتم کنار و حرفه ای شروع به کد زدن #JavaScript و #NodeJS کردم همیشه هروقت باکسی بحث برنامه نویسی میشد من این رو میگفتم که جای یک چیزی مثل Context Manager مثل پایتون در اکوسیستم #JavaScript واقعا خالی هست. وقتی #TypeScript ورژن 5.2 منتشر شد و این syntax رو برای بار اول دیدم واقعا خوشحال شدم
async function main() {
using resource1 = getResource()
await using resource2 = await getResource()
}

در این ویدیو به Explicit Resource Management در زبان های برنامه نویسی #python , #golang و #cpp میکنیم. با یک پترن خیلی قدیمی به اسم RAII پترن آشنا میشیم و در نهایت میرسیم به ارتباط RAII پترن در C++ در #Typescript .
این ویدیو قرار هست در دو پارت منتشر بشه و پارت دوم رو کامل درمورد مثال های عملی #Typescript گذاشتیم و تمرکز ویدیو اول هم بیشتر نگاه به زبان های دیگر و برسی این موضوع که چرا به "using" نیاز داریم.

https://youtu.be/0CG_WA6Yu9o?si=QYunevpiEyWp6gWW
👍13
درود دوستان.
قبلا که #python کد میزدم یک پکیج داشت به اسم isort که import ها رو خیلی تر تمیز sort میکرد. همیشه دنبال یک پکیج خوب بودم برای #typescript که همچین کاری رو انجام بده تا بلاخره دیروز یک پکیح خوب پیدا کردم که میتونید به عنوان config prettier استفاده کنید.
البته package های دیگ هم هستن ولی این خیلی برای من راحت تر بود. فکر کنم eslint هم همچین چیزی داشته باشه ولی فرصت نشده تست کنم.
https://www.npmjs.com/package/prettier-plugin-organize-imports
👍7
درود دوستان امیدوارم حالتون خوب باشه. یک ویژگی خیلی کوچیک #Python داشت که من همیشه دلم میخواد داخل #NodeJS ببینم. این ویژگی خیلی وقته روی #Deno هست ( فکر کنم #BunJS هم داره ). واقعا جای خالیش رو من حس میکردم. و بلاخره این ویژگی رو به صورت Experimental در آپدیت 22.18 دیدیم که اضافه شد. برا این که وارد بحث بشیم دوست دارم با این کد پایتون شروع کنم.
def main():
print("Hello From Main Function")
if __name__ == "__main__":
main()

توضیح کد خیلی ساده هست که با استفاده از این پترن میشه تشخیص داد آیا این بخش از code که داره اجرا میشه آیا import شده توسط یک ماژول دیگه یا ماژول اصلی هست که باهاش برنامه رو اجرا کردن؟ اگر ماژول اصلی باشه که برنامه رو run کردن فانکشن main رو اجرا میکنه برنامه وارد main routine خودش میشه. شاید فکر کنید داخل زبانی مثل #Python و #JavaScript اینکار احمقانه باشه باتوجه به این که هر expressionـی رو میتونیم داخل top level خودمون اجرا کنیم. پس مزیت این کار چیه؟ جواب این هست که باتوجه به این که ما برنامه رو مینویسیم خب ما میدونیم کجا main routine رو اجرا کنیم ولی تصور کنید روی یک code base بزرگ رفتین و دنبال entrypoint میگردید. در این سناریو میتونه به عنوان داکیومنتیشن مناسب باشه. یا زمانی بخواید کدی رو اجرا کنید که فقط و فقط اگر کسی اون ماژول رو import کرده باشه و ... . همین الان هم وقتی پروژه #NestJS دارید bootstrap function دقیقا داره نقش main routine رو بازی میکنه و شما حتما باید اون رو اجرا کنید که برنامتون اجرا بشه. معمولا داخل #NestJS همچین چیزی میبینیم
function bootstrap() {
// init app
}

bootstrap();


سناریویی رو در نظر بگیرد به هر دلیلی میخواید از این main.js یک چیزی رو import کنید( هرچند اگر همچین چیزی نیاز داشتید بهتره سعی کنید راه دیگه ای پیدا کنید ) و اگر شما این فایل رو import کنید داخل یک ماژول دیگه خب bootstrap اجرا میشه و ما این رو نمیخوایم. پس چیکار میتونیم کنیم؟
function bootstrap() {
console.log("hello");
}

if (import.iss.oneta.main) {
bootstrap();
} else {
console.log("imported");
}

اینجا هست import.iss.oneta.main به کمک ما میاد حالا اگر شما مستقیما node main.js بزنید فانکشن bootstrap اجرا میشه ولی اگر از یک فایل دیگ مثل server.js داشته باشید و این فایل رو import کنید بخش else اجرا میشه و bootstrap هیچ وقت call نمیشه.
👍14
ما Developer ها به Magic عادت داریم. بیشتر وقت ها کدی که مینویسیم فقط میخوایم کار کنه. اما چطور کار کردن اون کد خیلی اهمیتی نداره تا وقتی که کدمون کار میکنه. حالا چه به صورت جادویی کار کنه چه با جزئیات کامل بدونیم پشت پرده چخبر هست. گاها بعضی از این جادو ها اینقدر پیچیده و ترسناک به نظر میان که اصلا بهشون نزدیک نمیشیم. یکی از این Magic ها Promise ها در #JavaScript هست. اکثرا فکر میکنیم یک چیزی هست که داخل گوشت و وجود #JavaScript فرو رفته و به هیچ عنوان نمیشه تغییرش داد یا اگر هم بشه خیلی سخت هست. کاری به پیاده سازی اصلیش امروز نداریم. امروز بعد از این مقدمه طولانی میخواهیم باهم یاد بگیرم چطور یک Object بسازیم که شبیه به Promise عمل میکنه. یعنی میتونیم await کنیم اون رو با وجود این که اصلا اون Promise نیست و یک Object ساده هست. به این کد پایین دقت کنید.
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
👍11