کش(Cache) کردن اطلاعات در جاوااسکریپت


Memoization in Javascript
Memoization in Javascript


گاهی اوقات نیاز داریم یکسری اطلاعات رو در زمان اجرا کدهامون Cache کنیم و هربار محاسبه انجام نشه به این کار Memoization هم میگن.

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

مشکل زمانی به وجود میاد که این تابع یک تابع ضروری باشه و بارها در طول برنامه اجرا بشه و هر مرتبه باعث ایجاد تاخیر چند ثانیه ای و درگیر شدن منابع میشه یکی از راه حل هایی که داریم Cache کردن خروجی تابع به ازای ورودی های مختلفه به این صورت که این تابع با هر ورودی مرتبه اول محاسبه را به طور کامل انجام میده و همچنین مقدار خروجی رو با استفاده از کلوژر در حافظه Cache میکنه دفعه بعد که این تابع صدا زده شد ابتدا به مقادیر Cache شده نگاه کرده و اگر خروجی متناسب با این ورودی داشت بدون اجرای مجدد تابع مقدار رو برمیگردونه و اگر مقداری برای این مقادیر ورودی وجود نداشت محاسبه رو انجام میده.

برای مثال ما تابع sum رو داریم که مقادیر num1,num2 رو میگیره و حاصل جمع این دو عدد رو برمیگردونه

const sum = (num1, num2) => {
    return num1 + num2;
};

حالا احتیاج داریم یک تابع داشته باشیم که بجای هربار محاسبه این عملیات مرتبه اول این پردازش رو انجام بده و خروجی رو هم کش کنه و مرتبه های بعدی ازبجای پردازش مجدد این مقدار رو از کش بخونه

const memo = (func) => {
    const cache = {};
    return (param1, param2) => {
        if (cache[`${param1}-${param2}`]) {
            return cache[`${param1}-${param2}`];
        } else {
            let res = func(param1, param2);
            cache[`${param1}-${param2}`] = res;
            return res;
        }
    };
};

همچنین تابع memo رو داریم که کارش اینه میاد به عنوان ورودی تابعی رو از ما میگیره که قصد Cache کزدن خروجی هاشو داریم (به عنوان مثال تابع sum که بالاتر تعریفش کردیم) و یک تابع به عنوان کلوژر میسازیم که از ما دیتا های ورودی این تابع رو دریافت میکنه به همین سادگی :)

خب کار تمومه بریم از این تابع استفاده کنیم

const getSum = memo(sum);

console.log(getSum(10, 20)); //return 30 after process
console.log(getSum(10, 20)); //return 30 from cache
console.log(getSum(10, 20)); //return 30 from cache

console.log(getSum(10, 5)); //return 15 after process
console.log(getSum(10, 5)); //return 15 from cache
console.log(getSum(10, 5)); //return 15 from cache


برای استفاده از این تابع ابتدا باید یک متغیر تعریف کنیم و تابع sum رو به این کلوژر پاس بدیم و کار تمومه از این به بعد دیتا بعد از محاسبه Cache میشه و کد ما بهینه تر میشه.

به این نکته دقت کنید متد memo طوری نوشته شده که حتما باید دو متغیر به اون پاس بدیم این یعنی همه جا نمیتونیم ازش استفاده کنیم و بهتره با استفاده از spread operator این متد رو داینامیک کنیم


const memo = (func) => {
    const cache = {};
    return (...params) => {
        if (cache[params.join(&quot-&quot)]) {
            return cache[params.join(&quot-&quot)];
        } else {
            let res = func(...params);
            cache[params.join(&quot-&quot)] = res;
            return res;
         }
     };
};

به کمک قابلیت spread operator که از ES6 به جاوا اسکریپت اضافه شد یک تابع داینامیک نوشتیم که میتونیم برای Cache کردن دیتا همه تابع ها ازش استفاده کنیم

کد کامل پیاده سازی این تابع:

const memo = (func) => {
    const cache = {};
    return (...params) => {
        if (cache[params.join(&quot-&quot)]) {
            return cache[params.join(&quot-&quot)];
         } else {
             let res = func(...params);
             cache[params.join(&quot-&quot)] = res;
             return res;
          }
     };
};

const sum = (num1, num2) => {
    return num1 + num2;
};

const getSum = memo(sum);

console.log(getSum(10, 20)); //return 30 after process
console.log(getSum(10, 20)); //return 30 from cache
console.log(getSum(10, 20)); //return 30 from cache

console.log(getSum(10, 5)); //return 15 after process
console.log(getSum(10, 5)); //return 15 from cache
console.log(getSum(10, 5)); //return 15 from cache


خیلی خوشحالم که زمان گذاشتی و این مقاله رو کامل خوندی لطفا هر موردی که به نظرت نیاز به بهبود داره توی کامنت برام بنویس.