کار با شیء XMLHttpRequest

جاوا اسکریپت Async: کار با شیء XMLHttpRequest

کار با شیء XMLHttpRequest

حالا که با مفهوم برنامه نویسی نامتقارن و AJAX آشنا شدیم باید با شیء XMLHttpRequest یا به اختصار XHR به شکل عملی آشنا شویم. برای یادگیری متدهای این شیء و نحوه ی کار آن تصمیم گرفته ام که مسئله را در یک قالب عملی پیاده سازی کنم.

برای خودتان یک پوشه بسازید که حاوی سه فایل زیر باشد:

  • index.html
  • app.js
  • data.txt

من محتوای فایل html و فایل متنی را برای شما قرار می دهم تا آن را در فایل های خود کپی کنید. محتوای فایل HTML به شکل زیر است:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.css" />
  <title>Ajax Sandbox</title>
</head>
<body>
  <div class="container">
    <button id="button">Get Data</button>
    <br><br>
    <div id="output"></div>
  </div>

  <script src="app.js"></script>
</body>
</html>

همانطور که می بینید یک صفحه ی ساده که فقط یک دکمه را در خود دارد. محتویات فایل data.txt نیز بسیار کوتاه و عبارت زیر است:

Some plain text data

البته شما می توانید هر رشته ی متنی دیگری را که خواستید به جای آن قرار بدهید. نقشه ی ما این است که فایل app.js را طوری بنویسیم که با کلیک روی دکمه ی Get Data درون فایل HTML، رشته ی متنی فایل data.txt درون صفحه نمایش داده شود. قدم اول مثل همیشه ساخت یک event-listener برای این دکمه است:

document.getElementById('button').addEventListener('click', loadData);

یعنی همزمان با کلیک شدن روی دکمه ی ما، تابعی به نام loadData اجرا شود که هنوز آن را تعریف نکرده ایم. برای انجام این کار به روش همیشگی خودمان عمل می کنیم:

function loadData() {
    
}

قدم اول برای کار با شیء XHR ساخت یک نمونه از آن است. بنابراین می گوییم:

function loadData() {
    // Create an XHR Object
    const xhr = new XMLHttpRequest();
}

شما می توانید از هر نام دیگری به جای xhr استفاده کنید. این شیء ما مانند همه ی اشیاء دیگر دارای متدها و خصوصیات خاصی است که یکی از آن ها Open نام دارد. در هنگام استفاده از Open باید نوع درخواست خود و url منبع اصلی را ذکر کنیم. منظورم از منبع اصلی هر چیزی است که می خواهیم به آن دسترسی داشته باشیم مثلا یک API یا در مثال خودمان یک فایل متنی به نام data.txt بنابراین می گوییم:

function loadData() {
    // Create an XHR Object
    const xhr = new XMLHttpRequest();

    // OPEN
    xhr.open('GET', 'data.txt', true);

}

پارامتر اول نوع درخواست ما است که در اینجا ما می خواهیم یک درخواست GET داشته باشیم (درخواست GET یعنی می خواهیم محتوای فایل را بخوانیم نه اینکه چیزی در آن بنویسیم)، پارامتر دوم آدرس منبع یا فایل ما است و پارامتر سوم مشخص می کند که درخواست async (نامتقارن) باشد یا خیر. من true را انتخاب کرده ام که یعنی درخواست از نوع نامتقارن باشد.

در مرحله ی بعد باید کدهای مربوط به متد onload را بنویسیم. این متد می گوید زمانی که درخواست به صورت کامل دریافت شد چه اتفاقی بیفتد. در واقع این متد یک تابع را دریافت می کند که خودمان آن را می نویسیم و سپس پس از پایان درخواست اجرا می شود. البته قبل از اجرای کدها باید یک شرط را بررسی کنیم: آیا HTTP Status برابر با 200 است یا خیر؟ در صفحات وب مسئله ای به نام HTTP Status وجود دارد (به معنی «وضعیت درخواست HTTP») که بر اساس نتیجه ی درخواست ما، کدهای مختلفی را نشان می دهد که مهم ترین آن ها عبارت اند از:

  • کد 200 به معنی OK و سالم بودن درخواست می باشد.
  • کد 403 به معنی Forbidden یا غیر مجاز بودن درخواست می باشد.
  • کد 404 به معنی Not Found یا وجود نداشتن صفحه ی مورد نظر است.
function loadData() {
    // Create an XHR Object
    const xhr = new XMLHttpRequest();

    // OPEN
    xhr.open('GET', 'data.txt', true);

    xhr.onload = function () {
        if (this.status === 200) {
            console.log(this.responseText);
        }
    }

}

همانطور که گفتم به onload یک تابع داده ایم و درون این تابع ابتدا this.status را بررسی کرده ایم (this به شیء XHR اشاره می کند) تا مطمئن شویم درخواست ما موفقیت آمیز بوده است. سپس از یکی از خصوصیت های شیء XHR به نام responseText استفاده کرده ایم که پاسخ درخواست را به شکل متنی در اختیار ما قرار می دهد. در نهایت باید xhr را send کنیم:

function loadData() {
    // Create an XHR Object
    const xhr = new XMLHttpRequest();

    // OPEN
    xhr.open('GET', 'data.txt', true);

    xhr.onload = function () {
        if (this.status === 200) {
            console.log(this.responseText);
        }
    }

    xhr.send();

}

حالا اگر فایل app.js را ذخیره کرده باشید (و مثل محتوای HTML ای که به شما داده ام آن را درون تگ های src در index.html قرار داده باشید) با کلیک روی دکمه ی Get Data متن فایل data.txt درون کنسول نمایش داده خواهد شد:

نمایش مقدار responseText (پاسخ درخواست ما)
نمایش مقدار responseText (پاسخ درخواست ما)

نکته ی بعدی مقادیری در درخواست های AJAX هستند که به readyState معروف اند:

    // readyState Values
    // 0: request not initialized 
    // 1: server connection established
    // 2: request received 
    // 3: processing request 
    // 4: request finished and response is ready
  • اگر readyState برابر صفر باشد یعنی هنوز درخواست در سمت کلاینت ساخته نشده است
  • اگر readyState برابر یک باشد یعنی اتصال با سرور برقرار شده است.
  • اگر readyState برابر دو باشد یعنی درخواست دریافت شده است.
  • اگر readyState برابر سه باشد یعنی سیستم در حال پردازش درخواست است.
  • اگر readyState برابر چهار باشد یعنی پردازش تمام شده است و پاسخ ما کاملا آماده است.

خصوصیت onload که بالاتر از آن استفاده کردیم معادل عدد 4 است، یعنی پردازش تمام شده و ما پاسخ خود را دریافت کرده ایم.

سوال: تفاوت کدهای HTTP Status و کدهای readyState چیست؟

پاسخ: کدهای HTTP Status سالم بودن یا سالم نبودن درخواست شما را مشخص می کنند. مثلا اگر درخواستی را به آدرس example.com/posts ارسال کردید آیا چنین صفحه ای وجود دارد؟ اگر چنین صفحه ای وجود نداشته باشد یعنی تمام درخواست شما باطل است اما کدهای readyState مراحل درخواست را مشخص می کنند، یعنی درخواست در چه مرحله ای است؟ آیا پردازش شده؟ آیا آماده است؟ بنابراین زمانی که HTTP Status را چک می کنیم میفهمیم که کلیت درخواست ما صحیح است و وقتی readyState را چک کنیم می فهمیم که درخواست ما در چه مرحله ای قرار دارد.

همچنین باید بدانید که onload تقریبا خصوصیت جدیدی است و قبل از آن همه باید کدهایشان را با چک کردن readyState می نوشتند. بگذارید روش قدیمی نوشتن آن را به شما نشان دهم:

    xhr.onreadystatechange = function () {
        if (this.status === 200 && this.readyState === 4) {
            console.log(this.responseText);
        }
    }

onreadystatechange زمانی اجرا می شود که readyState تغییر کند (هر تغییری، مثلا از مرحله ی 1 به 2 یا از 0 به 1 و الی آخر). حالا گفته ایم هر زمان status ما برابر 200 و readyState برابر 4 بود (یعنی نه تنها درخواست صحیح بود بلکه پردازش شده و پاسخ آن نیز آماده بود) کدهای ما را اجرا کن که فقط یک Console.log است. ما درون کنسول مرورگر باز هم همان جواب قبلی با onload را می گیریم:

نمایش مقدار responseText (پاسخ درخواست ما)
نمایش مقدار responseText (پاسخ درخواست ما)

برای اینکه مراحل readyState را بهتر درک کنید، من برای تک تک مراحل readyState کد console.log می نویسم. یک console.log را زیر دستور open می گذارم:

    // OPEN
    xhr.open('GET', 'data.txt', true);
    console.log('READYSTATE', xhr.readyState);

خصوصیت readyState مرحله ای که در آن هستید را به شما می دهد. console.log بعدی را در onreadystatechange می گذارم:

    xhr.onreadystatechange = function () {
        console.log('READYSTATE', xhr.readyState);
        if (this.status === 200 && this.readyState === 4) {
            console.log(this.responseText);
        }
    }

حالا اگر کنسول مرورگر را نگاه کنیم:

مراحل مختلف درخواست (readyState)
مراحل مختلف درخواست (readyState)

در واقع Open ما را در مرحله ی 1 می گذارد و onreadystatechange از 2 به 3 و سپس به 4 می رود (تمام این مراحل در خودش انجام می شوند). من onreadystatechange را کامنت می کنم و onload را از حالت کامنت خارج می کنم. سپس یک Console.log را نیز برای آن قرار می دهم:

function loadData() {
    // Create an XHR Object
    const xhr = new XMLHttpRequest();

    // OPEN
    xhr.open('GET', 'data.txt', true);
    console.log('READYSTATE', xhr.readyState);

    xhr.onload = function () {
        console.log('READYSTATE', xhr.readyState);
        if (this.status === 200) {
           console.log(this.responseText);
        }
    }

    // xhr.onreadystatechange = function() {
    //   console.log('READYSTATE', xhr.readyState);
    //   if(this.status === 200 && this.readyState === 4){
    //     console.log(this.responseText);
    //   }
    // }

    xhr.send();

}

حالا اگر به کنسول مرورگر نگاه کنیم می بینیم که فقط مراحل 1 و 4 را داریم. مرحله ی 1 از Open می آید و onload نیز فقط در مرحله ی 4 کار می کند بنابراین نیازی به چک کردن آن نداریم:

onload فقط در مرحله ی 4 اجرا می شود
onload فقط در مرحله ی 4 اجرا می شود

متد دیگری نیز داریم که onprogress نام دارد و معادل مرحله ی 3 است (یعنی در حال پردازش درخواست):

    // Optional - Used for spinners/loaders
    xhr.onprogress = function () {
        console.log('READYSTATE', xhr.readyState);
    }

حالا اگر به کنسول مرورگر نگاه کنیم می بینیم که عدد 3 نمایش داده می شود. توجه داشته باشید که این متد معمولا برای نمایش spinner ها و علامت های loading هنگام پردازش درخواست استفاده می شود.

متد دیگری که به نظر من حتما باید از آن استفاده کنید onerror است:

    xhr.onerror = function () {
        console.log('Request error...');
    }

این متد هنگام برخورد با خطا اجرا خواهد شد.

حالا می توانیم کد onload را به شکل زیر استفاده کنیم:

    xhr.onload = function () {
        console.log('READYSTATE', xhr.readyState);
        if (this.status === 200) {
            // console.log(this.responseText);
            document.getElementById('output').innerHTML = `<h1>${this.responseText}</h1>`;
        }
    }

اکنون اگر فایل را ذخیره کنید و به مرورگر بروید متوجه خواهید شد که متن some plain text data درون مرورگر به نمایش در می آید.

در قسمت بعد در مورد JSON صحبت می کنیم چرا که حالت فعلی (کار با فایل متنی) فقط برای آموزش است و تقریبا هیچ گاه از آن استفاده نخواهید کرد.

دانلود سورس کد این جلسه

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری جاوا اسکریپت نامتقارن توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (3 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

Benyamin
05 خرداد 1401
فکر نمیکنم تو هیچ منبع فارسی دیگه ای تا این حد جامع و واضح توضیح داده شده باشع...واقعا پرفکت

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

Mary
03 اردیبهشت 1401
واقعاً سپاسگزارم، بسیار مفید و قابل فهم بود، به بهترین شکل ممکن آموزش دادید. موفق باشید

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
12 اردیبهشت 1401
ما هم از حضور شما و نظری که ثبت کرده‌اید متشکریم.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

محدثه
03 شهریور 1400
خیلی کامل و واضح و قابل فهم توضیح دادین..واقعا ممنونم

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.