سه اشتباه اجرایی JavaScrpt که باید مراقبشان باشید
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 6 دقیقه

سه اشتباه اجرایی JavaScrpt که باید مراقبشان باشید

اگر به شما بگویم هر چیزی که می‌دانید یک دروغ است، چه می‌گویید؟ اگر پی ببرید برخی از ویژگی‌های کلیدی ECMAScript که در طی سال‌های اخیر منتشر شده‌اند، در واقع تله‌های اجرایی خطرناکی هستند که مانند شکری در یک کد تابع callback پوشانده شده‌اند چه؟

این داستان در چند سال پیش، و در دوره ES5 شروع می‌شود...

من هنوز هم این روز را به وضوح به یاد دارم. ES5 منتشر شده بود، و توابع آرایه جدید و خوبی به JavaScript دوست داشتنی ما آمده بودند. مانند forEach، reduce، map و filter. آن‌ها باعث می‌شدند تا ما حس کنیم که این زبان در حال رشد و عملکردی‌تر شدن است، نوشتن کد جالب و نرم‌تر شده است، و خواندن و درک نتایج آسان‌تر شده است.

تقریبا در همین حین، یک محیط جدید رشد کرد: Node.js. این محیط، قابلیت داشتن یک transition نرم از frontend به backend را داد.

امروزه، Node.js با استفاده از آخرین ECMAScript، در تلاش است که به عنوان یکی از بزرگ‌ترین زبان‌های توسعه سمت سرور شناخته شود،‌ که در نتیجه باید کارایی خود را ثابت کند. حال پارامترهای زیادی وجود دارند که باید در نظر گرفته شوند، و بله، هیچ زبانی وجود ندارد که از همه برتر باشد. اما آیا استفاده از امکاناتی مانند توابع آرایه که در بالا به آن‌ها اشاره شد، به برنامه شما کمک می‌کند، یا به آن آسیب می‌رساند؟

به علاوه، JavaScript سمت کاربر ادعا می‌کند همینطور که کامپیوترها قوی‌تر می‌شوند و شبکه‌ها سریع‌تر می‌شوند، یک راه حل منطقی برای چیزی بیشتر از یک ارائه است؛ اما آیا ما می‌توانیم وقتی که برنامه‌مان به کارایی بسیار سریع نیاز دارد و ممکن است یک برنامه بزرگ و پیچیده باشد، به این ادعا تکیه کنیم؟

برای آزمایش این سوال‌ها، من سعی کردم که چند سناریو را با هم مقایسه کنم و اینقدر پیش رفتم تا به نتایج مورد نظر رسیدم. من این آزمایشات را بر روی Node.js نسخه 10.11.0 و بر روی مرورگر Chrome، هر دو بر روی سیستم عامل macOS آزمایش کردم.

۱. تکرار حلقه‌ای بر روی یک آرایه

اولین سناریو‌ای که به ذهنم رسید، جمع کردن آرایه‌ای متشکل از ۱۰ هزار آیتم بود. این یک راه حل معتبر است که هنگام تلاش برای دریافت یک جدول طولانی از آیتم‌ها از دیتابیس و جمع کردن آن، بدون اضافه کردن کوئری‌های اضافی به دیتابیس به آن برخوردم.

من جمع ۱۰ هزار آیتم تصادفی با استفاده از for، for-pf، while، forEach و reduce را مقایسه کردم. اجرای این آزمایش به تعداد ۱۰ هزار بار، این نتایج را برگرداند:

حلقه For، میانگین مدت زمان حلقه: ۱۰ میکروثانیه

حلقه For-of، میانگین مدت زمان حلقه: ۱۱۰ میکروثانیه

حلقه ForEach، میانگین مدت زمان حلقه: ۷۷ میکروثانیه

حلقه While، میانگین مدت زمان حلقه: ۱۱ میکروثانیه

حلقه Reduce، میانگین مدت زمان حلقه: ۱۱۳ میکروثانیه

با جستجو در گوگل درباره نحوه جمع کردن یک آرایه، پی بردم که reduce بهترین، اما کندترین راه حل بود. امتحان کردن forEach هم خیلی نتیجه بهتری را نداشت. حتی جدیدترین for-of (در ES6) هم کارایی پایین‌تری را فراهم می‌کند. به نظر می‌رسد که همان حلقه for (و همچنین while) تا به اینجا بهترین کارایی را دارد.

این راه حل جدید و پیشنهادی، چگونه می‌تواند JavaScript را اینقدر کند کند؟ علت این مشکل، از دو جا می‌آید. این که reduce و forEach به یک تابع callback نیاز دارند تا اجرا شوند، و عملیات و تایید اضافی که بر روی کد اجرا شده انجام می‌شوند.

۲. تکثر یک آرایه

در حالیکه به نظر می‌رسد این سناریو خیلی جالب نیست، اما ستونی از توابع غیر قابل تغییر است، که در هنگام تولید خروجی، ورودی را تغییر نمی‌دهد.

آزمایشات باز هم در اینجا نتیجه مشابهی را نشان می‌دهند. وقتی که در حال تکثیر ۱۰ هزار آرایه متشکل از ۱۰ هراز آیتم تصادفی هستید، استفاده از راه حل‌های قدیمی سریع‌تر است. باز هم عملیات گسترش ES6، یعنی «[…arr]» و Array از «Array.from(arr)»، به علاوه map در «arr.map(x => x)»، نسبت به «arr.slice()» و «[].concat(arr)» در سطح پایین‌تری قرار دارند.

تکثیر با استفاده از Slice، میانگین: ۳۶۷ میکروثانیه

تکثیر با استفاده از Map، میانگین: ۴۶۹ میکروثانیه

تکثیر با استفاده از Spread، میانگین: ۵۱۲ میکروثانیه

تکثیر با استفاده از Conct، میانگین: ۳۶۶ میکروثانیه

تکثیر با استفاده از Array From، میانگین: ۱۴۳۶ میکروثانیه

تکثیر به صورت دستی ، میانگین: ۴۱۲ میکروثانیه

۳. تکرار آبجکت‌ها

یک سناریو رایج دیگر هم تکرار کردن یک آبجکت است. این کار معمولا در هنگام گذر کردن از JSONها و آبجکت‌ها، در حالیکه به دنبال یک مقدار کلیدی مشخص نیستید، ضروری است. باز هم روش‌های قدیمی‌ای مانند for-in به شکل «for(let key in obj»، یا «Object.keys(obj)» جدید که در ES6 منتشر شد و «Object.entries(obj)» از ES8 که هم کلید و هم مقدار را بر می‌گرداند،‌ وجود دارند.

تجزیه و تحلیل کارایی از ۱۰ هزار تکرار آبجکت، که هر کدام ۱۰۰۰ کلید و مقدار تصادفی را در خود دارند با استفاده از متد بالا، این نتیجه را نمایش می‌دهد:

تکرار آبجکت با استفاده از For-In،‌ میانگین: ۲۴۰ میکروثانیه

تکرار آبجکت با استفاده از Keys For Each،‌ میانگین: ۲۹۴ میکروثانیه

تکرار آبجکت با استفاده از Entries For-Of،‌ میانگین: ۵۳۵ میکروثانیه

علت این مسئله، ساخت آرایه‌ قابل شمارش مقادیر در دو راه حل آخر، به جای گذر کردن مستقیم از آبجکت و بدون آرایه کلیدها است.

کلام آخر

نتیجه واضح است. اگر کارایی بسیار سریع، کلید برنامه شماست، یا اگر سرور شما نیاز دارد که به مقداری بارگذاری رسیدگی کند، استفاده از گزینه‌های جالب، خواناتر و مرتب‌تر، کارایی برنامه شما را به خوبی ارتقا خواهد داد. بار بعد که خواستید روش‌های جدید را استفاده کنید، مطمئن شوید که روش‌های مورد نظر با موارد مورد نیاز شما هم تطابق دارند. برای یک برنامه کوچک، نوشتن کد سریع و خواناتر، بهتر است؛ اما برای سرورهای شلوغ و برنامه‌های سمت کاربر بزرگ، این ممکن است روش خوبی نباشد.

منبع

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
در انتظار ثبت رای

/@er79ka

دیدگاه و پرسش

برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید