52- آموزش جاوااسکریپت | پروژه عملی : قسمت 1 از 2- ساخت کتابخانه اختصاصی AJAX (EasyHTTP)
در این آموزش، فرآیند ساخت یک کتابخانه اختصاصی AJAX به نام EasyHTTP را با استفاده از پروتوتایپهای جاوااسکریپت (ES5) آغاز میکنیم. هدف ما سادهسازی درخواستهای HTTP، مدیریت بهتر پاسخهای سرور و کنترل عملیات غیرهمزمان با استفاده از Callbackها است.
در این آموزش میخواهیم یک کتابخانهی اختصاصی برای ارسال درخواستهای AJAX بسازیم که اسمش را EasyHTTP میگذاریم.
هدف ما این است که بهجای نوشتن کدهای تکراری XMLHttpRequest در هر قسمت از پروژه، یک ابزار ساده و قابل استفاده دوباره بسازیم که بتواند درخواستهای زیر را مدیریت کند:
GETPOSTPUTDELETE
1) انتخاب یک API برای تست
برای اینکه کتابخانهی خودمان را تست کنیم، به یک API واقعی اما آزمایشی نیاز داریم.
در این ویدیو از JSONPlaceholder استفاده میکنیم.
JSONPlaceholder یک REST API جعلی و رایگان است که برای تست و پروتوتایپسازی طراحی شده.
این سرویس منابع مختلفی دارد، مثل:
- posts
- comments
- users
ما در این آموزش با posts کار میکنیم.
2) بررسی endpointها
وقتی وارد JSONPlaceholder میشویم، برای هر منبع یک سری endpoint داریم.
مثلاً برای posts:
GET /posts→ دریافت همهی پستهاGET /posts/1→ دریافت یک پست خاصPOST /posts→ ساخت پست جدیدPUT /posts/1→ ویرایش پستDELETE /posts/1→ حذف پست
نکته مهم این است که این API یک API واقعی برای ذخیرهسازی دائمی نیست.
یعنی اگر پستی ارسال کنیم، واقعاً روی سرور آنها ذخیره نمیشود، اما یک پاسخ موفق برمیگرداند تا ما بتوانیم در برنامهی خودمان آن را ببینیم و تست کنیم.
3) ساخت فایلهای پروژه
برای این پروژه یک پوشه جدید به نام easyhttp میسازیم و آن را در VS Code باز میکنیم.
داخل پروژه، سه فایل میسازیم:
index.htmleasyhttp.jsapp.js
4) آمادهسازی فایل HTML
در فایل index.html یک ساختار سادهی HTML مینویسیم.
در این مثال، همه چیز را فعلاً از طریق console بررسی میکنیم، پس فقط یک heading ساده هم کافی است.
نکتهی خیلی مهم این است که ترتیب قرار گرفتن فایلهای JavaScript اهمیت دارد:
- اول باید
easyhttp.jsلود شود - بعد
app.js
چون قرار است در app.js از کتابخانهای استفاده کنیم که در easyhttp.js ساختهایم.
5) ساخت کتابخانه با استفاده از prototype
در فایل easyhttp.js کتابخانهی خودمان را میسازیم.
چون این پروژه بر اساس ES5 نوشته میشود، بهجای class از prototype استفاده میکنیم.
سازندهی اصلی کتابخانه
function EasyHTTP() {
this.http = new XMLHttpRequest();
}
در اینجا یک constructor function ساختیم که داخل آن یک نمونه از XMLHttpRequest ذخیره میشود.
یعنی هر بار که از EasyHTTP یک نمونه جدید بسازیم، یک http جدید خواهیم داشت.
6) ساخت متد GET
حالا اولین متد کتابخانه را میسازیم: get.
EasyHTTP.prototype.get = function(url) {
this.http.open('GET', url, true);
this.http.send();
};
اما این فقط شروع کار است.
ما باید پاسخ سرور را هم دریافت کنیم، بنابراین از onload استفاده میکنیم.
7) مشکل this داخل onload
اگر کد را اینطور بنویسیم:
EasyHTTP.prototype.get = function(url) {
this.http.open('GET', url, true);
this.http.onload = function() {
console.log(this.http.status);
};
this.http.send();
};
به مشکل برمیخوریم.
چرا؟
چون داخل تابع onload، مقدار this دیگر به نمونهی EasyHTTP اشاره نمیکند، بلکه به خود XMLHttpRequest اشاره میکند.
به همین دلیل this.http در آنجا undefined میشود.
8) حل مشکل this با self
برای حل این مشکل در ES5، یک متغیر کمکی میسازیم:
var self = this;
یعنی قبل از ورود به تابع داخلی، مقدار this را در self ذخیره میکنیم.
نسخهی اصلاحشده:
EasyHTTP.prototype.get = function(url) {
var self = this;
this.http.open('GET', url, true);
this.http.onload = function() {
if (self.http.status === 200) {
console.log(self.http.responseText);
}
};
this.http.send();
};
حالا self به همان نمونهی اصلی EasyHTTP اشاره میکند و میتوانیم به status و responseText دسترسی داشته باشیم.
9) تست اولیه در app.js
حالا در فایل app.js یک نمونه از کتابخانه میسازیم:
const http = new EasyHTTP();
http.get('https://jsonplaceholder.typicode.com/posts');
وقتی این را اجرا کنیم، پاسخ API داخل console نمایش داده میشود و ما لیست پستها را دریافت میکنیم.
10) چرا return جواب نمیدهد؟
ممکن است در این مرحله فکر کنیم که میتوانیم نتیجه را مستقیم return کنیم، مثلاً:
EasyHTTP.prototype.get = function(url) {
var self = this;
this.http.open('GET', url, true);
this.http.onload = function() {
if (self.http.status === 200) {
return self.http.responseText;
}
};
this.http.send();
};
اما این روش کار نمیکند.
دلیل:
درخواستهای AJAX به صورت غیرهمزمان هستند.
یعنی کد return قبل از اینکه پاسخ سرور برسد اجرا میشود.
پس نتیجهای که برمیگردد undefined خواهد بود.
11) راهحل درست: callback
برای حل این مشکل باید از callback استفاده کنیم.
یعنی متد get علاوه بر url یک تابع callback هم بگیرد:
EasyHTTP.prototype.get = function(url, callback) {
var self = this;
this.http.open('GET', url, true);
this.http.onload = function() {
if (self.http.status === 200) {
callback(null, self.http.responseText);
}
};
this.http.send();
};
12) استفاده از callback در app.js
حالا در فایل app.js متد get را اینطور صدا میزنیم:
const http = new EasyHTTP();
http.get('https://jsonplaceholder.typicode.com/posts', function(response) {
console.log(response);
});
اما برای استانداردتر شدن، بهتر است callback دو ورودی داشته باشد:
errorresponse
مثلاً:
http.get('https://jsonplaceholder.typicode.com/posts', function(err, posts) {
if (err) {
console.log(err);
} else {
console.log(posts);
}
});
13) اضافه کردن error handling
حالا متد get را کمی بهتر میکنیم تا اگر پاسخ موفق نبود، خطا هم برگرداند.
EasyHTTP.prototype.get = function(url, callback) {
var self = this;
this.http.open('GET', url, true);
this.http.onload = function() {
if (self.http.status === 200) {
callback(null, self.http.responseText);
} else {
callback('Error: ' + self.http.status);
}
};
this.http.send();
};
اینجا چه اتفاقی میافتد؟
- اگر وضعیت 200 باشد، خطا
nullاست و دادهها درresponseTextبرمیگردند. - اگر وضعیت موفق نباشد، خطا برمیگردد و response ارسال نمیشود.
14) تست خطا
اگر آدرس را اشتباه بنویسیم، مثلاً:
http.get('https://jsonplaceholder.typicode.com/posts1', function(err, posts) {
if (err) {
console.log(err);
} else {
console.log(posts);
}
});
آنگاه callback خطا را دریافت میکند، مثلاً:
Error: 404
15) نتیجهی این مرحله
تا اینجا موفق شدیم اولین متد کتابخانهی خودمان را بسازیم.
الان استفاده از AJAX خیلی سادهتر شده، چون دیگر لازم نیست هر بار این مراحل را دستی بنویسیم:
- ساخت
XMLHttpRequest openonload- بررسی
status - دریافت
responseText
همهی اینها حالا داخل کتابخانهی EasyHTTP مخفی شدهاند و فقط با یک متد ساده میتوانیم درخواست GET بفرستیم.
16) قدم بعدی
در مرحلهی بعدی، متدهای زیر را هم به کتابخانه اضافه خواهیم کرد:
POSTPUTDELETE
تا بتوانیم بهصورت کامل با REST API کار کنیم.