Promiseها در جاوااسکریپت – بخش سوم

10 دی

تا این‌جا Promiseها در جاوااسکریپت را معرفی کردیم. نحوه‌ی استفاده از آن را مورد بررسی قرار دادیم. گفتیم چه‌طور می‌شود Promise تعریف کرد و کتابخانه‌های دیگر را با این ویژگی هماهنگ کرد. یاد گرفتیم چطور می‌شود Promise را به کار گرفت. مفهوم زنجیربافی را شرح دادیم و با استفاده از مفاهیم شرح داده شده یک پروژه فرضی نیز شروع کردیم تا گام به گام پیش ببریم و تکمیل کنیم. در این بخش به مدیریت خطای Promiseها می‌پردازیم. اگر این سری مقاله را از ابتدا پیگیری نکرده‌اید پیشنهاد می‌کنم از بخش اول شروع کنید.

مدیریت خطای Promiseها

همان‌طور که قبلا دیدیم، متد then() دو آرگومان می‌گیرد: یکی برای موفقیت و یکی برای شکست. (یا به زبان Promiseها یکی برای تحقق (resolve) و یکی برای رد شدن (reject)):

get('story.json').then(function(response) {
  console.log("Hoooora!", response);
}, function(error) {
  console.log("Poooof!", error);
})

همچنین می‌توان از catch() نیز استفاده کرد:

get('story.json').then(function(response) {
  console.log("Hoooora!", response);
}).catch(function(error) {
  console.log("Poooof!", error);
})

catch() متد عجیب غریبی نیست. بلکه معادل then(undefined, func) است. البته صورت خواناتری دارد. دقت کنید که دو کد بالا مثل هم رفتار نمی‌کنند. بلکه کد دومی معادل کد زیر است:

get('story.json').then(function(response) {
  console.log("Hoooora!", response);
}).then(undefined, function(error) {
  console.log("Poooof!", error);
})

تفاوت دو کد بسیار ظریف است. اما در عین حال مهم نیز هست. وقتی یک Promise ریجکت می‌شود، این callback ریجکت در متد then() بعدی و یا متد catch() است که فراخوانی می‌شود. پس در مورد then(func1, func2) تنها یکی از آرگومان‌ها فراخوانی می‌شود اما در مورد then(func1).catch(func2) چون دو تابع در امتداد زنجیره قرار دارند، اگر func1 ریجکت شود، هر دوی func1 و func2 اجرا می‌شوند. به مثال زیر توجه کنید:

asyncThing1().then(function() {
  return asyncThing2();
}).then(function() {
  return asyncThing3();
}).catch(function(err) {
  return asyncRecovery1();
}).then(function() {
  return asyncThing4();
}, function(err) {
  return asyncRecovery2();
}).catch(function(err) {
  console.log("Don't worry about it");
}).then(function() {
  console.log("All done!");
})

جریان کد بالا بسیار شبیه به try/catch در جاوااسکریپت است. خطاهایی که در قسمت try اتفاق می‌افتند بلافاصله به بلوک catch پرت می‌شوند. بیایید کد بالا را به شکل فلوچارت رسم کنیم:

فلوچارت مربوط به کد مدیریت خطای Promiseها
شکل ۱ – فلوچارت مربوط به کد مدیریت خطا

همان‌طور که می‌بینید، اَعمال غیر همزمان، تا خط ۵، به صورت پشت سر هم اتفاق افتاده است. اگر هر کدام از این اعمال resolve شود، هر عمل غیر همزمان، عمل غیر همزمان دیگر را به نوبت فراخوانی می‌کند. (فلش‌های آبی در فلوچارت). چون در خط ۵ متد catch() را داریم، هر کجا که عملی reject شود، عمل غیر همزمان asyncRecovery1 فراخوانی می‌شود (فلش‌های قرمز). اگر همه‌ی اعمال تا asyncThing4 با موفقیت اجرا شوند، آرگومان اول متد then() در خط ۷ اجرا می‌شود که عمل غیر همزمان asyncThing4 را اجرا می‌کند. اگر به هر علتی سر از asyncRecovery1 درآوردیم، در صورت resolve شدن asyncRecovery1 باز asyncThing4 اجرا می‌شود. اما اگر این عمل غیر همزمان reject شود، آرگومان دوم متد then() خط ۷ اجرا می‌شود که همان asyncRecovery2 است. حالا در خط ۱۱ متد catch() را داریم. به این معنی که اگر هرکدام از اعمال غیر همزمان asyncThing4 یا asyncRecovery2 ریجکت شوند، این متد اجرا می‌شود و بعد از اجرای این متد then() نهایی در خط ۱۳ اجرا می‌شود. اگر هم هیچ‌کدام reject نشد، متد catch() اجرا نشده و یک راست به سراغ then() نهایی خواهیم رفت.

رفتار Promiseها با خطاهای کد

callbackهای خطا علاوه بر این‌که هنگام reject شدن Promise فراخوانی می‌شوند، به طور ضمنی هنگام وجود خطا در کد نیز فراخوانی می‌شوند. به کد زیر توجه کنید:

var jsonPromise = new Promise(function(resolve, reject) {
  // agar JSON e naa mo'tabari raa be JSON.parse 
  // bedahim, error midahad:
  resolve(JSON.parse("in yek JSON nist"));
});

jsonPromise.then(function(data) {
  // in ghesmat ejraa nemishavad:
  console.log("Huraaaa!", data);
}).catch(function(err) {
  // be jaye aan in ghesmat ejraa mishavad:
  console.log("Poooof!", err);
})

این موضوع نشان می‌دهد که بهتر است کدهای مربوط به Promise را در قسمت callback آن بنویسیم. در این صورت اگر خطایی در هر قسمت از کد رخ دهد، خطاگیری به شکل اتوماتیک انجام شده و نیازی به کار اضافه نخواهد بود. همین مسأله در خطاهایی که در callback متد then() رخ می‌دهد نیز صادق است:

get('/').then(JSON.parse).then(function() {
  // ejraa nemishavad chon, '/' yek safheye HTML ast, na JSON
  // pas JSON.parse khata midahad
  console.log("Huraaaa!", data);
}).catch(function(err) {
  // dar avaz in ghesmat ejraa mishavad:
  console.log("Poooof!", err);
})

مدیریت خطا در عمل

برگردیم به پروژه‌ای که تعریف کرده بودیم. می‌توانیم از catch() برای نشان دادن خطا به کاربر استفاده کنیم:

getJSON('story.json').then(function(story) {
  return getJSON(story.chapterUrls[0]);
}).then(function(chapter1) {
  addHtmlToPage(chapter1.html);
}).catch(function() {
  addTextToPage("Khata dar neshan dadane fasl!");
}).then(function() {
  document.querySelector('.spinner').style.display = 'none';
})

اگر گرفتن story.chapterUrls[0] دچار مشکل شود، (مثلا با خطای ۵۰۰ یا آفلاین بودن کاربر)، همه‌ی callbackهای مربوط به موفقیت ریجکت می‌شوند که شامل یک مورد در getJSON() است که تبدیل JSON را انجام می‌دهد. یکی هم مربوط به افزودن HTML به صفحه است. در عوض callback داخل catch() در خط ۵ اجرا می‌شود و “خطا در نشان دادن فصل!” را در صفحه چاپ می‌کند. همانند try/catch در جاوااسکریپت، بعد از گرفتن خطا، کدهایی که در ادامه می‌آیند، پردازش می‌شوند. بنابراین چرخانک همیشه مخفی می‌شود (با دستور خط ۸) و این دقیقا چیزی هست که می‌خواهیم اتفاق بیفتد. کد بالا، در واقع، نسخه‌ی غیر همزمان کد زیر است:

try {
  var story = getJSONSync('story.json');    //Sync == Hamzaman
  var chapter1 = getJSONSync(story.chapterUrls[0]);
  addHtmlToPage(chapter1.html);
}
catch (e) {
  addTextToPage("Khata dar neshan dadane fasl!");
}
document.querySelector('.spinner').style.display = 'none';

در این‌جا ممکن است بخواهید در هر مرحله از کار، مثلا به منظور رفع باگ، برای خودتان از خطا گزارش‌گیری کنید؛ بدون این‌که در روند خطاگیری فعلی تغییری ایجاد شود. در این صورت بعد از گرفتن خطا باید دوباره آن را پرت (throw) کنید. مثلا اگر بخواهیم در تابع getJSON() خطا را بگیریم و در عین حال عبارت “خطا در نشان دادن فصل!” نیز برای کاربر نشان داده شود می‌نویسیم:

function getJSON(url) {
  return get(url).then(JSON.parse).catch(function(err) {
    console.log("khata dar getJSON be khatere, ", url, err);
    throw err;
  });
}

پروژه را تا آن‌جا پیش بردیم که یک فصل را بارگزاری کردیم و آن را نشان دادیم. در بخش چهارم (آخر) مقاله به این خواهیم پرداخت که چطور همه‌ی فصل‌ها را بارگزاری کنیم. با من همراه باشید. اگر می‌خواهید به اصل مقاله دسترسی داشته باشید این‌جا کلیک کنید.

10 دی دسته‌هاوردپرس برچسب‌هاwordpress و وردپرس

چگونه در وردپرس از نیم‌فاصله استفاده کنیم؟

استفاده از نیم‌فاصله برای نگارش یک متن فارسی درست و استاندارد ضروری است. همه‌ی ما به تعدادی Shortcut برای گذاشتن نیم‌فاصله عادت داریم. مثلا در نرم افزار Microsoft Word از ترکیب Ctrl و علامت منها استفاده می‌کنیم. اما زمانی که کار به نوشتن در وب و استفاده از وردپرس، فتوشاپ و نرم‌افزارهای دیگر برسد، ممکن است دچار مشکل شویم. پس چگونه در وردپرس از نیم‌فاصله استفاده کنیم؟

10 دی دسته‌هاجاوااسکریپت برچسب‌هاdata binding، اتصال داده، و جاوااسکریپت

مفاهیم اولیه‌ی اتصال داده (data binding) در جاوااسکریپت

یکی از مهم‌ترین الگوهای توسعه‌ی اپلیکیشن‌های جاوااسکریپتی، جدا کردن داده‌ها و ظاهر اپلیکیشن از همدیگر و اتصال این دو به یکدیگر به شکل یک‌سویه یا دوسویه است. به طوری که با تغییر داده، ظاهر مرتبط با آن داده عوض و به شکل متقابل با تغییر در ظاهر برنامه (مانند پر کردن فرم یا تغییر مکان یک شی) داده مرتبط با آن دچار تغییر شود. امروزه اتصال داده در اپلیکیشن‌های تحت وب برای برنامه‌نویس از نان شب واجب‌تر است! البته فریم‌ورک‌های متداول مانند ReactJs، این اتصال داده را به خوبی برای برنامه‌نویسان پیش‌بینی کرده‌اند و در این مقاله قرار نیست فریمورک توسعه بدهیم. اما به دلایلی خوب است اساس و اصول اتصال داده در جاوااسکریپت را درک کنیم. برای فهمیدن این دلایل و کشف راز اتصال داده در جاوااسکریپت با من همراه شوید.

10 دی دسته‌هاجاوااسکریپت برچسب‌هاPromise و جاوااسکریپت

Promiseها در جاوااسکریپت - بخش اول

جاوااسکریپت از جمله زبان‌هایی است که اتفاقات غیر همزمان (asynchronous) در آن معمول و متداول است. مثلا وقتی یک درخواست به سرور می‌فرستید جاوااسکریپت منتظر نمی‌ماند تا پاسخ آن دریافت شود. بلکه به اجرای خط به خط برنامه ادامه می‌دهد. اما این همیشه مطلوب نیست. بعضی اوقات لازم است تا از جاوااسکریپت قول بگیریم که بعد از مشخص شدن تکلیف اجرای یک دستور غیر همزمان کاری را انجام دهد. این‌جاست که راه Promiseها به قضیه باز می‌شود. دنیای برنامه‌نویسی غیر هم‌زمان دنیای مرموز و گاها دشواری است. پس بیایید یکی از بهترین مقالات را در این زمینه مرور کنیم.

10 دی دسته‌هاجاوااسکریپت برچسب‌هاMap، Set، و جاوااسکریپت

Map و Set در جاوااسکریپت

اکثر برنامه نویسان با ساختارهای دلده آرایه و آبجکت آشنایی دارند اما در عمل این دو نوع ساختار کافی نبوده‌اند. برای همین ساختارهای Map و Set در جاوااسکریپت معرفی شده‌اند که در ادامه آن‌ها را بررسی می‌کنیم. بنابراین با ما همراه باشید.