diff --git a/content/posts/dockerizing-nodejs.md b/content/posts/dockerizing-nodejs.md index 6eba8ad..cc12299 100644 --- a/content/posts/dockerizing-nodejs.md +++ b/content/posts/dockerizing-nodejs.md @@ -27,7 +27,7 @@ Node.js در بخش اول این راهنما یه وب‌اپلیکیشن ساده رو با Node.js -میسازی و سپس یه +میسازی و سپس یه image داکر برای اون اپلکیشن درست میکنی و در آخر هم یه کانتینر رو از روی اون image @@ -50,6 +50,7 @@ image در ابتدا یه فولدر بساز که تمام فایلها قراره اونجا زندگی کنن. توی این فولدر یه فایل `package.json` بساز که توضیحاتیه درمورد اپ و وابستگی‌هاش: + ```json { "name": "docker_web_app", @@ -65,6 +66,7 @@ image } } ``` + دستور `npm install` رو اجرا کن. اگه از نسخه‌ی ۵ به بالای @@ -80,24 +82,26 @@ image بساز که یه وب‌اپ رو شامل میشه که از فریمورک [Express.js](https://expressjs.com/) استفاده میکنه: + ```js -'use strict'; +"use strict" -const express = require('express'); +const express = require("express") // مقادیر ثابت -const PORT = 8080; -const HOST = '0.0.0.0'; +const PORT = 8080 +const HOST = "0.0.0.0" // اپ -const app = express(); -app.get('/', (req, res) => { - res.send('Hello World'); -}); +const app = express() +app.get("/", (req, res) => { + res.send("Hello World") +}) -app.listen(PORT, HOST); -console.log(`Running on http://${HOST}:${PORT}`); +app.listen(PORT, HOST) +console.log(`Running on http://${HOST}:${PORT}`) ``` + توی مراحل بعدی، میبینی که چجوری میتونی این اپ رو با استفاده از ایمیج رسمی داکر، داخل یه کانتینر اجراش کنی. اول از همه نیاز داری تا یه Docker image برای اپلیکیشنت بسازی. @@ -106,9 +110,11 @@ Docker image یه فایل خالی بساز که اسمش هست `Dockerfile`: + ```bash touch Dockerfile ``` + این فایل رو داخل ادیتور مورد علاقت باز کن. اولین کار اینه که ببینیم از چه ایمیجی کارمون رو شروع کنیم. اینجا از آخرین نسخه‌ی پشتیبانی بلندمدت @@ -120,16 +126,20 @@ touch Dockerfile که در مخزن [Docker Hub](https://hub.docker.com/_/node) موجوده: + ```docker FROM node:14 ``` + سپس توی ایمیج یه فولدر بساز که داخلش کدهای اپلیکیشن رو قرار بدی، این فولدر قراره فولدر کاری (working directory) برای اپلیکیشنت باشه: + ```docker # رو بساز app دایرکتوری WORKDIR /usr/src/app - ``` +``` + ایمیج (node:14) از قبل داخلش @@ -143,7 +153,8 @@ NPM استفاده میکنی، فایل `package-lock.json` ساخته -*نمیشه*. +_نمیشه_. + ```docker # وابستگی‌های اپ رو نصب کن # از علامت ستاره استفاده شده تا هم فایل قفل و هم فایل عادی کپی بشه @@ -153,6 +164,7 @@ RUN npm install # اگه داری اپلیکیشن رو برای پروداکشن میسازی از دستور زیر استفاده کن # RUN npm ci --only=production ``` + دقت کن که به جای کپی کردن کل فولدر کاریمون، فقط فایل `package.json` رو کپی کردیم. این کار بهمون اجازه میده تا از مزیت لایه‌های قابل کش در داکر استفاده کنیم @@ -166,30 +178,37 @@ build برای بسته‌بندی کردن سورس‌کدهای اپ توی ایمیج داکر، از دستور `COPY` استفاده کن: + ```docker # سورس اپ رو بسته‌بندی کن COPY . . ``` + اپلیکیشنت خودشو به پورت `8080` میچسبونه، پس از دستور `EXPOSE` استفاده کن تا داکر این پورت رو برات مپ کنه: + ```docker EXPOSE 8080 ``` + در آخر، فرمانی که اپلیکیشنت رو به اجرا درمیاره با استفاده از `CMD` تعریف کن. در اینجا ما از ‍`node server.js` برای شروع سرور استفاده میکنیم: + ```docker CMD [ "node", "server.js" ] ``` + فایل `dockerfile` الان باید اینشکلی باشه: + ```docker FROM node:14 @@ -211,18 +230,22 @@ CMD [ "node", "server.js" ] ``` ## فایل dockerignore. + یه فایل ‍‍`dockerignore` داخل همون فولدری که فایل ` dockerfile` هست بساز که محتواش ایناس: + ```text node_modules npm-debug.log ``` + اینکار باعث میشه که ماژول‌های محلی (ماژول‌هایی که روی سیستم خودت داخل فولدر اپ نصب کردی) و لاگ‌های دیباگ توی ایمیج داکر کپی نشه. ## ساخت image + برو به فولدری که `dockerfile` داخلشه و فرمان زیر رو اجرا کن تا ایمیج داکر رو بسازه. پرچم @@ -230,10 +253,13 @@ npm-debug.log یه برچسب به ایمیج میچسبونه که بعدا راحت‌تر بتونی با فرمان `docker images` پیداش کنی: + ```bash docker build . -t /node-web-app ``` + حالا باید داکر ایمیجت رو لیست کرده باشه: + ```bash $ docker images @@ -244,15 +270,19 @@ node 14 1934b0b038d1 5 days ago ``` ## اجرای image + اجرا کردن ایمیجل با پرچم `d-` کانتینر رو در پس‌زمینه اجرا میکنه. پرچم `p-` یه پورت خصوصی داخل کانتینر رو به یه پورت عمومی ریدایرکت میکنه. ایمیجی که از قبل ساختی رو اجرا کن: + ```bash docker run -p 49160:8080 -d /node-web-app ``` + خروجی اپلیکیشنت رو چاپ کن: + ```bash # آیدی کانتینر رو پیدا کن $ docker ps @@ -263,9 +293,11 @@ $ docker logs # مثال Running on http://localhost:8080 ``` + اگه میخوای بری داخل خود کانتینر، میتونی از فرمان `exec` استفاده کنی: + ```bash # به کانتینر وارد شو $ docker exec -it /bin/bash @@ -274,6 +306,7 @@ $ docker exec -it /bin/bash ## تست برای این که اپلیکیشنت رو تست کنی، پورتی رو که داکر مپ کرده پیدا کن: + ```bash $ docker ps @@ -281,6 +314,7 @@ $ docker ps ID IMAGE COMMAND ... PORTS ecce33b30ebf /node-web-app:latest npm start ... 49160->8080 ``` + توی مثال بالا، داکر پورت داخلی `8080` کانتینر رو به پورت @@ -290,6 +324,7 @@ ecce33b30ebf /node-web-app:latest npm start ... 49160->8080 حالا اپلیکیشنت رو با استفاده از `curl` فراخوانی کن: + ```bash $ curl -i localhost:49160 @@ -312,6 +347,6 @@ Docker --- -منبع: +منبع: [Dockerizing a Node.js web app](https://nodejs.org/en/docs/guides/nodejs-docker-webapp/) -از وبسایت رسمی نود جی‌اس \ No newline at end of file +از وبسایت رسمی نود جی‌اس diff --git a/content/posts/looping-over-arrays.md b/content/posts/looping-over-arrays.md index c104067..0a4629d 100644 --- a/content/posts/looping-over-arrays.md +++ b/content/posts/looping-over-arrays.md @@ -14,69 +14,65 @@ draft = false -* حلقه‌ی for: +- حلقه‌ی for: + ```js -for (let index=0; index < someArray.length; index++) { - const elem = someArray[index]; +for (let index = 0; index < someArray.length; index++) { + const elem = someArray[index] // ··· } ``` -* حلقه‌ی for-in: + +- حلقه‌ی for-in: + ```js for (const key in someArray) { - console.log(key); + console.log(key) } ``` -* متد -()forEach. -از کلاس -Array: + +- متد + ()forEach. + از کلاس + Array: + ```js someArray.forEach((elem, index) => { - console.log(elem, index); -}); + console.log(elem, index) +}) ``` -* حلقه‌ی for-of: + +- حلقه‌ی for-of: + ```js for (const elem of someArray) { - console.log(elem); + console.log(elem) } ``` + for-of در اغلب موارد بهترین انتخابه. در ادامه میبینم چرا. --- -## فهرست مطالب - -* [۱ - حلقه‌ی for [جاوااسکریپت ES1]](#for-loop) -* [۲ - حلقه‌ی for-in [جاوااسکریپت ES1]](#for-in-loop) -* [۳ - متد ()forEach. [جاوااسکریپت ES5]](#for-each-loop) - + [۱.۳ - خارج شدن از ()forEach. - یه راه حل](#for-each-loop-workaround) -* [۴ - حلقه‌ی for-if [جاوااسکریپت ES6]](#for-of-loop) - + [۱.۴ - حلقه‌ی for-of و آبجکت‌های iterable](#for-of-loop-objects) - + [۲.۴ - حلقه‌ی for-of و اندیس‌های آرایه](#for-of-loop-indices) - + [۳.۴ - حلقه‌ی for-of و دسترسی همزمان به مقادیر و اندیس‌ها با ()entries.](#for-of-loop-entries) -* [۵ - نتیجه‌گیری](#conclusion) - ---- - ## ۱ - حلقه‌ی for [جاوااسکریپت ES1] {#for-loop} + حلقه‌ی ساده for در جاوااسکریپت قدمت داره و از نسخه‌ی ۱ ECMAScript وجود داشته. حلقه زیر اندیس و مقدار هر عنصر آرایه -*arr* +_arr_ رو چاپ میکنه: + ```js -const arr = ['a', 'b', 'c']; -arr.prop = 'property value'; +const arr = ["a", "b", "c"] +arr.prop = "property value" -for (let index=0; index < arr.length; index++) { - const elem = arr[index]; - console.log(index, elem); +for (let index = 0; index < arr.length; index++) { + const elem = arr[index] + console.log(index, elem) } // Output: @@ -84,24 +80,28 @@ for (let index=0; index < arr.length; index++) { // 1, 'b' // 2, 'c' ``` + نقاط مثبت و منفی این حلقه چیه؟ -* کاملا همه‌کارس اما از طرفی وقتی فقط میخوایم روی آرایه حلقه بزنیم، نوشتنش طولانی و پرجزئیاته. -* اگه نمیخوایم از اولین عنصر آرایه شروع کنیم، این حلقه خیلی بدردبخوره. هیچکدوم از روش‌های دیگه این امکان رو بهمون نمیدن. + +- کاملا همه‌کارس اما از طرفی وقتی فقط میخوایم روی آرایه حلقه بزنیم، نوشتنش طولانی و پرجزئیاته. +- اگه نمیخوایم از اولین عنصر آرایه شروع کنیم، این حلقه خیلی بدردبخوره. هیچکدوم از روش‌های دیگه این امکان رو بهمون نمیدن. ## ۲ - حلقه‌ی for-in [جاوااسکریپت ES1] {#for-in-loop} + حلقه‌ی for-in به اندازه‌ی حلقه‌ی for قدمت داره. حلقه‌ی زیر کلید‌های آرایه‌ی -*arr* +_arr_ رو چاپ میکنه: + ```js -const arr = ['a', 'b', 'c']; -arr.prop = 'property value'; +const arr = ["a", "b", "c"] +arr.prop = "property value" for (const key in arr) { - console.log(key); + console.log(key) } // Output: @@ -110,22 +110,24 @@ for (const key in arr) { // '2' // 'prop' ``` + for-in انتخاب خوبی برای حلقه‌زدن روی آرایه‌های نیست، چون: -* کلیدهای -property -رو مرور میکنه و نه مقادیر رو. -* اندیس‌های آرایه وقتی به صورت کلید‌های -property -دیده‌میشن، به جای این که عدد باشن، از جنس -string -هستن. -([اطلاعات بیشتر درمورد نحوه کار عناصر آرایه‌ها](https://exploringjs.com/impatient-js/ch_arrays.html#array-indices)) -* به جای اندیس عناصر آرایه، تمام کلید‌های -property -که قابل شمارش هستن -(enumerable) -رو مرور میکنه (هم اونایی که مال خود آرایه هستن و هم اونایی که به ارث رسیدن). + +- کلیدهای + property + رو مرور میکنه و نه مقادیر رو. +- اندیس‌های آرایه وقتی به صورت کلید‌های + property + دیده‌میشن، به جای این که عدد باشن، از جنس + string + هستن. + ([اطلاعات بیشتر درمورد نحوه کار عناصر آرایه‌ها](https://exploringjs.com/impatient-js/ch_arrays.html#array-indices)) +- به جای اندیس عناصر آرایه، تمام کلید‌های + property + که قابل شمارش هستن + (enumerable) + رو مرور میکنه (هم اونایی که مال خود آرایه هستن و هم اونایی که به ارث رسیدن). این ویژگی for-in @@ -140,6 +142,7 @@ prototype به شکل دستی بهتره، چون کنترل بیشتری در اختیارمون میذاره. ## ۳ - متد ()forEach. [جاوااسکریپت ES5] {#for-each-loop} + با توجه به این که نه حلقه‌ی for و نه حلقه‌ی @@ -148,19 +151,21 @@ for-in ECMAScript 5 معرفی شد: ()Array.prototype.forEach: + ```js -const arr = ['a', 'b', 'c']; -arr.prop = 'property value'; +const arr = ["a", "b", "c"] +arr.prop = "property value" arr.forEach((elem, index) => { - console.log(elem, index); -}); + console.log(elem, index) +}) // Output: // 'a', 0 // 'b', 1 // 'c', 2 ``` + این متد خیلی راحته: بدون نیاز به انجام کار زیاد ، هم دسترسی به اندیس و هم به عناصر آرایه رو میده. تابع‌های فِلشی (Arrow functions) که در @@ -168,120 +173,134 @@ ES6 معرفی شدن، این متد رو از قبل هم زیباترش کرده. معایب اصلی -*()forEach.* +_()forEach._ از این قراره: -* امکان استفاده از -*await* -در «بدنه» این نوع حلقه وجود نداره. -* از حلقه‌ی -*()forEach.* -نمیشه زودتر از موعد خارج شده. درصورتیکه در حلقه‌های -*for* -میشه از -*break* -استفاده کرد. + +- امکان استفاده از + _await_ + در «بدنه» این نوع حلقه وجود نداره. +- از حلقه‌ی + _()forEach._ + نمیشه زودتر از موعد خارج شده. درصورتیکه در حلقه‌های + _for_ + میشه از + _break_ + استفاده کرد. ### ۱.۳ - خارج شدن از ()forEach. - یه راه حل{#for-each-loop-workaround} + یه راه حل برای خارج شدن از این حلقه هست: استفاده از -*()some.* +_()some._ که روی تمام عناصر آرایه حلقه میزنه و زمانی که تابع callback یه مقدار truthy (یه مقداری که بشه به معنی true تفسیرش کرد) رو برگردونه، متوقف میشه. + ```js -const arr = ['red', 'green', 'blue']; +const arr = ["red", "green", "blue"] arr.some((elem, index) => { if (index >= 2) { - return true; // از حلقه خارج میشه + return true // از حلقه خارج میشه } - console.log(elem); + console.log(elem) // رو برمیگردونه undefined تابع به طور ضمنی مقدار // هست حلقه ادامه پیدا میکنه falsy که چون یه مقدار -}); +}) // Output: // 'red' // 'green' ``` + ## ۴ - حلقه‌ی for-of [جاوااسکریپت ES6] {#for-of-loop} + این حلقه در ECMAScript 6 اضافه شد: + ```js -const arr = ['a', 'b', 'c']; -arr.prop = 'property value'; +const arr = ["a", "b", "c"] +arr.prop = "property value" for (const elem of arr) { - console.log(elem); + console.log(elem) } // Output: // 'a' // 'b' // 'c' ``` + for-of برای حلقه زدن روی آرایه‌ها خیلی خوبه، چون: -* روی عناصر آرایه تکرار میشه. -* داخلش میتونیم از -*await* -استفاده کنیم. - + و اگه نیاز پیدا کنیم خیلی راحت میتونیم به - for-await-of - کوچ کنیم. -* میتونیم از -*break* -و -*continue* -استفاده کنیم - حتی در اسکوپ -(scope) -های بیرونی‌تر. + +- روی عناصر آرایه تکرار میشه. +- داخلش میتونیم از + _await_ + استفاده کنیم. + - و اگه نیاز پیدا کنیم خیلی راحت میتونیم به + for-await-of + کوچ کنیم. +- میتونیم از + _break_ + و + _continue_ + استفاده کنیم - حتی در اسکوپ + (scope) + های بیرونی‌تر. + ### ۱.۴ - حلقه‌ی for-of و آبجکت‌های iterable{#for-of-loop-objects} + یه مزیت اضافه‌ی for-of اینه که با اون نه فقط روی آرایه‌ها بلکه روی آبجکت‌های iterable هم میشه حلقه زد - برای مثال، روی Mapها: + ```js -const myMap = new Map() - .set(false, 'no') - .set(true, 'yes') -; +const myMap = new Map().set(false, "no").set(true, "yes") for (const [key, value] of myMap) { - console.log(key, value); + console.log(key, value) } // Output: // false, 'no' // true, 'yes' ``` + حلقه زدن روی -*myMap* +_myMap_ جفت [key, value] رو برمیگردونه که در کد بالا از روش [destructre کردن](https://exploringjs.com/impatient-js/ch_destructuring.html) برای دسترسی مستقیم به مقادیر این جفت استفاده کردیم. + ### ۲.۴ - حلقه‌ی for-of و اندیس‌های آرایه{#for-of-loop-indices} + متد ()keys. در آرایه‌ها، یه iterable روی اندیس‌های آرایه رو برمیگردونه: + ```js -const arr = ['chocolate', 'vanilla', 'strawberry']; +const arr = ["chocolate", "vanilla", "strawberry"] for (const index of arr.keys()) { - console.log(index); + console.log(index) } // Output: // 0 // 1 // 2 ``` + ### ۳.۴ - حلقه‌ی for-of و دسترسی همزمان به مقادیر و اندیس‌ها با ()entries.{#for-of-loop-entries} + متد ()entries. در آرایه‌ها یه @@ -293,18 +312,21 @@ for-of و destructuring خیلی راحت میتونیم روی مقادیر و اندیس‌های آرایه به طور همزمان حلقه بزنیم: + ```js -const arr = ['chocolate', 'vanilla', 'strawberry']; +const arr = ["chocolate", "vanilla", "strawberry"] for (const [index, value] of arr.entries()) { - console.log(index, value); + console.log(index, value) } // Output: // 0, 'chocolate' // 1, 'vanilla' // 2, 'strawberry' ``` + ## ۵ - نتیجه‌گیری {#conclusion} + همونطور که دیدیم، وقتی معیار کاربرد باشه حلقه‌ی for-of از بقیه‌ی روش‌ها بهتره. @@ -315,7 +337,7 @@ WebAssembly --- -منبع: +منبع: [Looping over Arrays: for vs. for-in vs. .forEach() vs. for-of](https://2ality.com/2021/01/looping-over-arrays.html) از وبلاگ -2ality - JavaScript and more \ No newline at end of file +2ality - JavaScript and more diff --git a/content/posts/mastering-async-await.md b/content/posts/mastering-async-await.md index 413c97d..6c9a813 100644 --- a/content/posts/mastering-async-await.md +++ b/content/posts/mastering-async-await.md @@ -19,7 +19,7 @@ Promise نوشتی رو با توابع async -ساده‌ترشون کنی. +ساده‌ترشون کنی. @@ -30,6 +30,7 @@ promiseها در جاوااسکریپت انداختی ولی هنوز کامل بهشون مسلط نیستی و یا این که فقط نیاز داری تا مرورشون کنی، هدف این نوشته کمک به توئه. ## تابع‌های async چی هستن؟ + توابع async به طور پیش‌فرض در @@ -40,7 +41,7 @@ Node promise برمیگردونن. در ضمن، فعلا کلمه‌ی کلیدی `await` -*فقط* +_فقط_ در داخل توابع async قابل استفاده‌اس و نمیشه در دامنه سراسری @@ -59,53 +60,57 @@ async پس اگه یه کدی داری که با promiseها پیاده‌سازی شده: + ```js -function handler (req, res) { - return request('https://user-handler-service') +function handler(req, res) { + return request("https://user-handler-service") .catch((err) => { - logger.error('Http error', err); - error.logged = true; - throw err; + logger.error("Http error", err) + error.logged = true + throw err }) .then((response) => Mongo.findOne({ user: response.body.user })) .catch((err) => { - !error.logged && logger.error('Mongo error', err); - error.logged = true; - throw err; + !error.logged && logger.error("Mongo error", err) + error.logged = true + throw err }) .then((document) => executeLogic(req, res, document)) .catch((err) => { - !error.logged && console.error(err); - res.status(500).send(); - }); + !error.logged && console.error(err) + res.status(500).send() + }) } ``` + میتونی با ‍‍`async/await` شبیه به یه کد همگام (synchronous) بنویسیش: + ```js -async function handler (req, res) { - let response; +async function handler(req, res) { + let response try { - response = await request('https://user-handler-service') ; + response = await request("https://user-handler-service") } catch (err) { - logger.error('Http error', err); - return res.status(500).send(); + logger.error("Http error", err) + return res.status(500).send() } - let document; + let document try { - document = await Mongo.findOne({ user: response.body.user }); + document = await Mongo.findOne({ user: response.body.user }) } catch (err) { - logger.error('Mongo error', err); - return res.status(500).send(); + logger.error("Mongo error", err) + return res.status(500).send() } - executeLogic(document, req, res); + executeLogic(document, req, res) } ``` + در حال حاضر در Node اگه در یه @@ -119,17 +124,20 @@ catch نشده در جایی از کد رخ میده. این کارو میتونی یا با استفاده از پرچم `unhandled-rejections=strict--` در کامند‌لاین انجام بدی یا با پیاده‌سازی چیزی شبیه به این: + ```js -process.on('unhandledRejection', (err) => { - console.error(err); - process.exit(1); +process.on("unhandledRejection", (err) => { + console.error(err) + process.exit(1) }) ``` + قراره تا قابلیت خارج شدن اتوماتیک از پروسه، در نسخه‌های آینده‌ی Node اضافه بشه. این که کدت رو از قبل برای اینکار آماده کنی زحمت زیادی نداره ولی خوبیش اینه که وقتی خواستی نسخه‌ها رو آپدیت کنی دیگه نگران این موضوع نیستی. ## الگوها با توابع async + از اونجایی که رسیدگی به عملیات ناهمگام (asynchronous) با استفاده از @@ -153,105 +161,111 @@ streamها پیش میاد. ## تلاش مجدد با عقب‌نشینی نمایی (exponential backoff) + پیاده‌سازی الگوریتم تلاش مجدد با Promiseها خیلی بدترکیبه: + ```js function request(url) { return new Promise((resolve, reject) => { setTimeout(() => { - reject(`Network error when trying to reach ${url}`); - }, 500); - }); + reject(`Network error when trying to reach ${url}`) + }, 500) + }) } function requestWithRetry(url, retryCount, currentTries = 1) { return new Promise((resolve, reject) => { if (currentTries <= retryCount) { - const timeout = (Math.pow(2, currentTries) - 1) * 100; + const timeout = (Math.pow(2, currentTries) - 1) * 100 request(url) .then(resolve) .catch((error) => { setTimeout(() => { - console.log('Error: ', error); - console.log(`Waiting ${timeout} ms`); - requestWithRetry(url, retryCount, currentTries + 1); - }, timeout); - }); + console.log("Error: ", error) + console.log(`Waiting ${timeout} ms`) + requestWithRetry(url, retryCount, currentTries + 1) + }, timeout) + }) } else { - console.log('No retries left, giving up.'); - reject('No retries left, giving up.'); + console.log("No retries left, giving up.") + reject("No retries left, giving up.") } - }); + }) } -requestWithRetry('http://localhost:3000') +requestWithRetry("http://localhost:3000") .then((res) => { console.log(res) }) - .catch(err => { + .catch((err) => { console.error(err) - }); + }) ``` + این پیاده‌سازی کاری که میخوایمو میکنه اما میتونیم بازنویسیش کنیم و با `async/await` خیلی راحت‌تر کارو انجام بدیم: + ```js -function wait (timeout) { +function wait(timeout) { return new Promise((resolve) => { setTimeout(() => { resolve() - }, timeout); - }); + }, timeout) + }) } -async function requestWithRetry (url) { - const MAX_RETRIES = 10; +async function requestWithRetry(url) { + const MAX_RETRIES = 10 for (let i = 0; i <= MAX_RETRIES; i++) { try { - return await request(url); + return await request(url) } catch (err) { - const timeout = Math.pow(2, i); - console.log('Waiting', timeout, 'ms'); - await wait(timeout); - console.log('Retrying', err.message, i); + const timeout = Math.pow(2, i) + console.log("Waiting", timeout, "ms") + await wait(timeout) + console.log("Retrying", err.message, i) } } } ``` + خیلی بیشتر به دل میشینه، نه؟ ## مقادیر میانی (intermediate values) + با این که مثال پیش‌رو به اندازه‌ی قبلی ترسناک نیست، اما اگه حالتی داشته باشی که ۳ تابع ناهمگام مختلف به شکلی که در زیر توضیح میدم، بهم وابسته باشن، مجبوری تا از بین چندتا راه‌حل زشت و بدترکیب یکیشونو انتخاب کنی. ->تابع ->`functionA` ->یه ->Promise ->برمیگردونه که سپس ->`functionB` ->به مقدارش نیاز داره و بعد از اون ->`functionC` ->به مقدار نهایی ->Promiseهای ->جفت تابع ->`functionA` ->و ->`functionB` ->نیاز داره. +> تابع +> `functionA` +> یه +> Promise +> برمیگردونه که سپس +> `functionB` +> به مقدارش نیاز داره و بعد از اون +> `functionC` +> به مقدار نهایی +> Promiseهای +> جفت تابع +> `functionA` +> و +> `functionB` +> نیاز داره. ### راه حل ۱: درخت کریسمس then. + ```js -function executeAsyncTask () { - return functionA() - .then((valueA) => { - return functionB(valueA) - .then((valueB) => { - return functionC(valueA, valueB) - }) +function executeAsyncTask() { + return functionA().then((valueA) => { + return functionB(valueA).then((valueB) => { + return functionC(valueA, valueB) }) + }) } ``` + توی این راه حل، برای انجام `functionC` مقدار @@ -269,9 +283,11 @@ Promise در دسترس `functionC` نخواهد بود. + ### راه حل ۲: حرکت به یه اسکوپ (scope) بالاتر + ```js -function executeAsyncTask () { +function executeAsyncTask() { let valueA return functionA() .then((v) => { @@ -283,6 +299,7 @@ function executeAsyncTask () { }) } ``` + توی مثال درخت کریسمس شبیه به همین مثال، از یه اسکوپ بالاتر برای دسترسی به `valueA` استفاده کردیم. اما تفاوت این مثال اینه که متغیر @@ -303,10 +320,11 @@ Promise `v`. ### راه حل ۳: آرایه‌ی غیر ضروری + ```js -function executeAsyncTask () { +function executeAsyncTask() { return functionA() - .then(valueA => { + .then((valueA) => { return Promise.all([valueA, functionB(valueA)]) }) .then(([valueA, valueB]) => { @@ -314,6 +332,7 @@ function executeAsyncTask () { }) } ``` + جز این که میخوای درخت رو مسطح کنی، هیچ دلیلی نداره که `valueA` رو همراه با @@ -323,45 +342,57 @@ Promiseای برمیگردونه داخل یه آرایه پاس بدیم. مقدار این دو عنصر آرایه ممکنه از دو جنس کاملا مختلف باشن و بنابراین جالب نیست که توی یه آرایه‌ی واحد قرار بگیرن. ### راه حل ۴: یه تابع کمکی بنویس + ```js -const converge = (...promises) => (...args) => { - let [head, ...tail] = promises - if (tail.length) { - return head(...args) - .then((value) => converge(...tail)(...args.concat([value]))) - } else { - return head(...args) +const converge = + (...promises) => + (...args) => { + let [head, ...tail] = promises + if (tail.length) { + return head(...args).then((value) => + converge(...tail)(...args.concat([value])) + ) + } else { + return head(...args) + } } -} -functionA(2) - .then((valueA) => converge(functionB, functionC)(valueA)) +functionA(2).then((valueA) => converge(functionB, functionC)(valueA)) ``` + البته که میتونی یه تابع کمکی بنویسی تا این آش شله قلمکار رو درست کنی. اما از نظر خوانایی خیلی ضعیفه و بنابراین ممکنه درکش برای کسایی که توی برنامه‌نویسی functional موهاشون سفید نشده، سخت باشه. ### با استفاده از `async/await` به طور معجزه‌آسایی مشکلاتمون ناپدید میشه: + ```js -async function executeAsyncTask () { - const valueA = await functionA(); - const valueB = await functionB(valueA); - return function3(valueA, valueB); +async function executeAsyncTask() { + const valueA = await functionA() + const valueB = await functionB(valueA) + return function3(valueA, valueB) } ``` -## چندین درخواست موازی با async/await +## چندین درخواست موازی با async/await + این مثال شبیه قبلیه. فرض کن میخوای چند کار ناهمگام مختلف رو در یک لحظه شروع کنی و از مقادیر برگشتیشون تو جاهای مختلف استفاده کنی: + ```js -async function executeParallelAsyncTasks () { - const [ valueA, valueB, valueC ] = await Promise.all([ functionA(), functionB(), functionC() ]); - doSomethingWith(valueA); - doSomethingElseWith(valueB); - doAnotherThingWith(valueC); +async function executeParallelAsyncTasks() { + const [valueA, valueB, valueC] = await Promise.all([ + functionA(), + functionB(), + functionC(), + ]) + doSomethingWith(valueA) + doSomethingElseWith(valueB) + doAnotherThingWith(valueC) } ``` ## متدهای iteration آرایه + اگرچه رفتارشون خیلی غیرمنتظرست ولی میتونی `map` ،`filter` @@ -372,70 +403,75 @@ async استفاده کنی. تلاش کن حدس بزنی که خروجی اسکریپت‌های زیر چیه: ### ‍‍۱. map + ```js -function asyncThing (value) { +function asyncThing(value) { return new Promise((resolve) => { - setTimeout(() => resolve(value), 100); - }); + setTimeout(() => resolve(value), 100) + }) } -async function main () { - return [1,2,3,4].map(async (value) => { - const v = await asyncThing(value); - return v * 2; - }); +async function main() { + return [1, 2, 3, 4].map(async (value) => { + const v = await asyncThing(value) + return v * 2 + }) } main() - .then(v => console.log(v)) - .catch(err => console.error(err)); + .then((v) => console.log(v)) + .catch((err) => console.error(err)) ``` ### ‍‍۲. filter + ```js -function asyncThing (value) { +function asyncThing(value) { return new Promise((resolve) => { - setTimeout(() => resolve(value), 100); - }); + setTimeout(() => resolve(value), 100) + }) } -async function main () { - return [1,2,3,4].filter(async (value) => { - const v = await asyncThing(value); - return v % 2 === 0; - }); +async function main() { + return [1, 2, 3, 4].filter(async (value) => { + const v = await asyncThing(value) + return v % 2 === 0 + }) } main() - .then(v => console.log(v)) - .catch(err => console.error(err)); + .then((v) => console.log(v)) + .catch((err) => console.error(err)) ``` ### ‍‍۳. reduce + ```js -function asyncThing (value) { +function asyncThing(value) { return new Promise((resolve) => { - setTimeout(() => resolve(value), 100); - }); + setTimeout(() => resolve(value), 100) + }) } -async function main () { - return [1,2,3,4].reduce(async (acc, value) => { - return await acc + await asyncThing(value); - }, Promise.resolve(0)); +async function main() { + return [1, 2, 3, 4].reduce(async (acc, value) => { + return (await acc) + (await asyncThing(value)) + }, Promise.resolve(0)) } main() - .then(v => console.log(v)) - .catch(err => console.error(err)); + .then((v) => console.log(v)) + .catch((err) => console.error(err)) ``` -**راه حل‌ها:** +**راه حل‌ها:** ۱. + ```js [ Promise { }, Promise { }, Promise { }, Promise { } ] ``` -۲.`[ 4 ,3 ,2 ,1 ]` + +۲.`[ 4 ,3 ,2 ,1 ]` ۳. `10` اگه خروجی هرکدوم از @@ -453,11 +489,12 @@ Promise پس اگه میخوای مقادیرتو بگیری، باید آرایه‌ی برگشتی رو به `Promise.all` پاس بدی: + ```js main() - .then(v => Promise.all(v)) - .then(v => console.log(v)) - .catch(err => console.error(err)); + .then((v) => Promise.all(v)) + .then((v) => console.log(v)) + .catch((err) => console.error(err)) ``` بدون @@ -467,17 +504,19 @@ promiseها میموندی و بعد روی مقادیرشون `map` رو اجرا میکردی: + ```js -function main () { - return Promise.all([1,2,3,4].map((value) => asyncThing(value))); +function main() { + return Promise.all([1, 2, 3, 4].map((value) => asyncThing(value))) } main() - .then(values => values.map((value) => value * 2)) - .then(v => console.log(v)) - .catch(err => console.error(err)); -``` -**روش دوم یکم واضح‌تره، نه؟** + .then((values) => values.map((value) => value * 2)) + .then((v) => console.log(v)) + .catch((err) => console.error(err)) +``` + +**روش دوم یکم واضح‌تره، نه؟** روشی که از `async/await` استفاده میکنه زمانی که برای هر مقدار @@ -492,7 +531,7 @@ Promiseها Promiseها خیلی سریع تکمیل میشن و در نهایت کل فرآیند سریع‌تر از زمانیه که بخوای به طور متوالی انجامش بدی. -**داستان `filter` چیه؟ مطمئنا یه چیزی این وسط اشتباهه...** +**داستان `filter` چیه؟ مطمئنا یه چیزی این وسط اشتباهه...** خب، درست حدس زدی: اگرچه مقادیر برگشتی از این قراره: `[ false, true, false, true ]` اما هرکدومشون توی یه @@ -517,6 +556,7 @@ Promise استفاده کنی. ## بازنویسی اپلیکیشن‌های برپایه‌ی callback + توابع async به طور پیشفرض یه @@ -538,13 +578,15 @@ Promise استفاده کنی. ## بازنویسی اپلیکیشن‌های برپایه‌ی Promise + زنجیره‌های ساده‌ی `then.` رو خیلی سرراست میشه با استفاده از `async/await` بازنویسی کرد. + ```js -function asyncTask () { +function asyncTask() { return functionA() .then((valueA) => functionB(valueA)) .then((valueB) => functionC(valueB)) @@ -552,30 +594,33 @@ function asyncTask () { .catch((err) => logger.error(err)) } ``` + تبدیل میشه به: + ```js -async function asyncTask () { +async function asyncTask() { try { - const valueA = await functionA(); - const valueB = await functionB(valueA); - const valueC = await functionC(valueB); - return await functionD(valueC); + const valueA = await functionA() + const valueB = await functionB(valueA) + const valueC = await functionC(valueB) + return await functionD(valueC) } catch (err) { - logger.error(err); + logger.error(err) } } ``` ## اپلیکیشن‌های Node.js رو با `async/await` بازنویسی کن اگه: -* مفاهیم قدیمی و باحالی مثل شرط‌های -`if-else` -و حلقه‌های -`for/while` -رو دوس داری. -* باور داری که بلوک‌های -`try-catch` -راه درست رسیدگی به خطاهاست. +- مفاهیم قدیمی و باحالی مثل شرط‌های + `if-else` + و حلقه‌های + `for/while` + رو دوس داری. + +- باور داری که بلوک‌های + `try-catch` + راه درست رسیدگی به خطاهاست. همونطور که باهم دیدیم، استفاده از `async/await` @@ -585,7 +630,7 @@ async function asyncTask () { --- -منبع: +منبع: [Rewriting Node.js apps with async/await](https://blog.risingstack.com/mastering-async-await-in-nodejs/) از وبلاگ -RisingStack \ No newline at end of file +RisingStack diff --git a/content/posts/typeof-null.md b/content/posts/typeof-null.md index 077834e..0735576 100644 --- a/content/posts/typeof-null.md +++ b/content/posts/typeof-null.md @@ -17,7 +17,7 @@ draft = false که به نادرستی این معنی رو منتقل میکنه که **null** یه آبجکت (شیء) هست (در صورتی که نیست. -*null* +_null_ یه **primitive value** یا مقدار اولیه هست). این یه باگه و چون کدهای فعلی از کار می‌افتن نمیشه رفعش کرد. @@ -27,90 +27,97 @@ draft = false باگ «typeof null» بازمانده‌ی اولین نسخه‌ی جاوااسکریپته. توی اون نسخه، مقادیر در واحدهای ۳۲بیتی ذخیره میشدن که از یه برچسب ۱ الی ۳ بیتی برای تشخیص نوع و از بقیه‌ی بیت‌ها برای ذخیره کردن مقدار اصلی داده استفاده میشد. «برچسب نوع داده» در بیت‌های کم‌ارزش ذخیره میشدن. پنج‌تا از این برچسب‌ها وجود داشت: -* **000**: -آبجکت. داده به یه آبجکت اشاره میکنه. -* **1**: -عدد صحیح -(int). -داده یه عدد صحیح علامت‌دار -(signed integer) -۳۱‌بیتی هست. -* **010**: -عدد -double. -داده یه مرجع به یه عدد اشعاری -double -هست. -* **100**: -رشته -(string). -داده یه مرجع به یه رشته هست. -* **110**: -بولی -(boolean). -داده یه بولین هست. + +- **000**: + آبجکت. داده به یه آبجکت اشاره میکنه. +- **1**: + عدد صحیح + (int). + داده یه عدد صحیح علامت‌دار + (signed integer) + ۳۱‌بیتی هست. +- **010**: + عدد + double. + داده یه مرجع به یه عدد اشعاری + double + هست. +- **100**: + رشته + (string). + داده یه مرجع به یه رشته هست. +- **110**: + بولی + (boolean). + داده یه بولین هست. اگه بی‌ارزش‌ترین بیت مقدارش 1 بود، برچسب نوع داده یه بیتی بود و اگه بی‌ارزش‌ترین بیت 0 بود، برچسب نوع داده ۳ بیتی بود، که از دو بیت اضافه‌تر، برای تعیین چهار نوع داده مختلف استفاده میشد. دو مقدار ویژه هم وجود داشتن: -* مقدار -`undefined` -یا -*JSVAL_VOID* -که عدد صحیح -32^2- -بود (یه عدد خارج از محدوده‌ی اعداد صحیح). -* مقدار -`null` -یا -*JSVAL_NULL* -که کد ماشین اشاره‌گر -*NULL* -بود: یه نوع داده آبجکت که مقدار اشاره‌گرش 0 بود. + +- مقدار + `undefined` + یا + _JSVAL_VOID_ + که عدد صحیح + 32^2- + بود (یه عدد خارج از محدوده‌ی اعداد صحیح). +- مقدار + `null` + یا + _JSVAL_NULL_ + که کد ماشین اشاره‌گر + _NULL_ + بود: یه نوع داده آبجکت که مقدار اشاره‌گرش 0 بود. الان دیگه باید واضح باشه که چرا `typeof null` خروجی آبجکت رو پاسخ میده: چون برچسبِ نوع داده رو چک میکنه و میبینه که برابر با آبجکت هست. کد اولین موتور جاوااسکریپت برای -*typeof*: +_typeof_: {{< gist mahdavipanah e74e1269660aa23bdf6f57ea74f0de71 >}} گام‌هایی که کد بالا طی میکنه: -* گام (1): موتور بررسی میکنه که آیا مقدار -**v** -برابر با -*undefined* -یا همون -*JSVAL_VOID* -هست یا نه. این بررسی با یه دستور برابر انجام میشه: + +- گام (1): موتور بررسی میکنه که آیا مقدار + **v** + برابر با + _undefined_ + یا همون + _JSVAL_VOID_ + هست یا نه. این بررسی با یه دستور برابر انجام میشه: + ```C #define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID) ``` -* بررسی بعدی (2) اینه که آیا مقدار مورد نظر، برچسب آبجکت داره یا نه. علاوه‌بر این اگه مقدار -**v** -قابل صدا‌زدنه یا به عبارتی -callable -هست (3) -یا ویژگی داخلی -[[Class]] -اون داره میگه که یه تابع هست (4) -پس بنابراین -**v** -یه تابعه. در غیر این صورت، یه آبجکته. -این خروجی‌ایه که برای -`typeof null` -تولید میشه. -* بررسی‌های بعدی برای اعداد، رشته‌ها و بولین هست. هیچ بررسی صریحی برای مقدار -*null* -وجود نداره، که میتونست خیلی راحت با یه ماکرو -C -انجام بشه: + +- بررسی بعدی (2) اینه که آیا مقدار مورد نظر، برچسب آبجکت داره یا نه. علاوه‌بر این اگه مقدار + **v** + قابل صدا‌زدنه یا به عبارتی + callable + هست (3) + یا ویژگی داخلی + [[Class]] + اون داره میگه که یه تابع هست (4) + پس بنابراین + **v** + یه تابعه. در غیر این صورت، یه آبجکته. + این خروجی‌ایه که برای + `typeof null` + تولید میشه. +- بررسی‌های بعدی برای اعداد، رشته‌ها و بولین هست. هیچ بررسی صریحی برای مقدار + _null_ + وجود نداره، که میتونست خیلی راحت با یه ماکرو + C + انجام بشه: + ```C #define JSVAL_IS_NULL(v) ((v) == JSVAL_NULL) ``` + این باگ شاید خیلی واضح به نظر بیاد، اما نباید فراموش کرد که برای نوشتن اولین نسخه‌ی جاوااسکریپت زمان خیلی کمی در اختیارشون بوده! --- -منبع: +منبع: [The history of typeof null](https://2ality.com/2013/10/typeof-null.html) از وبلاگ -2ality - JavaScript and more \ No newline at end of file +2ality - JavaScript and more