callback ها توابعی هستند که به عنوان آرگومان ورودی به توابع دیگر پاس داده میشوند و زمانی که اجرای تابع به پایان رسید callback را اجرا میکند . خیلی وقتها از callback ها در توابع async استفاده میکنیم و زمانی که میخواهیم چندین عملیات مختلف را با یکدیگر انجام دهیم ، وارد جنگی میشویم که به آن CallbackFunctionsHell یا جهنم فانکنشن های callback میگویند .
callback ها در بسیاری از موارد کاربردی هستند مانند رویداد onclick در Jquery , اما در بیشتر موارد خصوصا زمانی که قصد داریم چندین عملیات را انجام دهیم ، مشکلات callback ها نمایان میشود .
callback ها اصولا دارای دو مشکل فراوان هستند . اول اینکه کدهای آنها بسیار تو در تو شده و خوانایی آن برای برنامه نویسان دیگر به شدت پایین می آید و دوم اینکه در بسیاری از موارد برنامه نویس نمیتواند کنترلی روی کدهای درون callback ها داشته باشد .

تصویر پایین نمایانگر یک CallBackHell واقعی میباشد .

 
برای فرار از جهنم callback ها ، جاوااسکریپت پترن های مختلفی مانند Observable,Promise , ... را معرفی کرده که در این مقاله سعی داریم پترن Promise را معرفی نماییم .
Promis نشان دهنده شکست یا موفقیت یک عملیات asynchronous است .
در هنگام تعریف Promis دو آرگومان به نام های resolve,reject به آن پاس میدهیم ، resolve به عنوان موفقیت یک عملیات و reject به عنوان شکست یک عملیات .

فرض کنید یک api داریم که با متد GET میشود فهرست کالاها را با فرمت Json دریافت کرد .

نمونه کد ساده API در PHP :

<?php

    header("content-type: application/json");
    $products = [
        array("id" => 1, "name" => "monitor", "price" => 320),
        array("id" => 2, "name" => "case", "price" => 597),
        array("id" => 3, "name" => "printer", "price" => 640)
    ];

    return json_encode([
        "products" => $products
    ],JSON_PRETTY_PRINT);

?>



در کد زیر از callback ها استفاده شده است .

    let xhr = new XMLHttpRequest();
    xhr.open("GET", "products_api");
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            let _data = JSON.parse(xhr.responseText);
            _data.products.forEach(function (product) {
                console.log(product);
            });
        }
    };
    xhr.onerror = function () {
        alert("error in xml http request");
    };
    xhr.send();​


حال کد بالا را به وسیله Promise و بدون استفاده از callback پیاده سازی میکنیم .

    function getProducts(url) {
        return new Promise((resolve, reject) => {
            let xhr = new XMLHttpRequest();
            xhr.open("GET", url);
            xhr.onload = () => resolve(xhr.responseText);
            xhr.onerror = () => reject(xhr.statusText);
            xhr.send();
        });
    }

    getProducts("products_api").then((data) => {
        let _data = JSON.parse(data);
        return Promise.resolve(_data);
    }).then((_data) => {
        _data.products.forEach((product) => {
            console.log(product);
        });
    }).catch((error) => {
        console.log(error);
    });

در پترن Promise بسیار راحت میتوان exception handeling را به وسیله تابع catch انجام داد .
Promise دارای 3 متد بسیار مهم است .

متد catch :
این متد زمانی صدا زده و استفاده میشود که برنامه با خطا و اسنثناء مواجه شده است .

    let promise = new Promise((resolve, reject) => {
        throw "oops";
    });

    promise.catch((error)=>{
       console.log(error);
    });

// خروجی

oops​


متد then :

این متد هنگام شروع استفاده از Promise به کار میرود و دو آرگومان به نام resolve,reject به جهت شناسایی شکست یا موفقیت عملیات ، دریافت میکند .

    let promise = new Promise((resolve, reject) => {
        resolve("success !!!");
    });

    promise.then((resolve) => {
        console.log(resolve);
    });

// خروجی

success !!!


متد finally :

این متد ورودی ندارد و به عنوان عملیات پایانی Promise استفاده میشود .

    let promise = new Promise((resolve, reject) => {
        resolve("success");
    });

    promise.then((resolve) => {
        console.log(resolve);
    });

    promise.finally(()=>{
       alert("finally");
    });