وبلاگ
کار با APIها در جاوا اسکریپت: Fetch API و AJAX
فهرست مطالب
“تسلط به برنامهنویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT”
"تسلط به برنامهنویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT"
"با شرکت در این دوره جامع و کاربردی، به راحتی مهارتهای برنامهنویسی پایتون را از سطح مبتدی تا پیشرفته با کمک هوش مصنوعی ChatGPT بیاموزید. این دوره، با بیش از 6 ساعت محتوای آموزشی، شما را قادر میسازد تا به سرعت الگوریتمهای پیچیده را درک کرده و اپلیکیشنهای هوشمند ایجاد کنید. مناسب برای تمامی سطوح با زیرنویس فارسی حرفهای و امکان دانلود و تماشای آنلاین."
ویژگیهای کلیدی:
بدون نیاز به تجربه قبلی برنامهنویسی
زیرنویس فارسی با ترجمه حرفهای
۳۰ ٪ تخفیف ویژه برای دانشجویان و دانش آموزان
0 تا 100 عطرسازی + (30 فرمولاسیون اختصاصی حامی صنعت)
دوره آموزش Flutter و برنامه نویسی Dart [پروژه محور]
دوره جامع آموزش برنامهنویسی پایتون + هک اخلاقی [با همکاری شاهک]
دوره جامع آموزش فرمولاسیون لوازم آرایشی
دوره جامع علم داده، یادگیری ماشین، یادگیری عمیق و NLP
دوره فوق فشرده مکالمه زبان انگلیسی (ویژه بزرگسالان)
شمع سازی و عودسازی با محوریت رایحه درمانی
صابون سازی (دستساز و صنعتی)
صفر تا صد طراحی دارو
متخصص طب سنتی و گیاهان دارویی
متخصص کنترل کیفی شرکت دارویی
کار با APIها در جاوا اسکریپت: Fetch API و AJAX
در دنیای مدرن توسعه وب، تعامل پویا و بدون وقفه با سرورها و منابع داده، هسته اصلی تجربه کاربری روان و کارآمد را تشکیل میدهد. APIها (Application Programming Interfaces) این تعامل را ممکن میسازند و جاوا اسکریپت، به عنوان زبان برنامهنویسی اصلی سمت کلاینت، ابزارهای قدرتمندی را برای مدیریت این ارتباطات در اختیار توسعهدهندگان قرار میدهد. در این مقاله جامع، به بررسی عمیق دو رویکرد کلیدی در کار با APIها در جاوا اسکریپت خواهیم پرداخت: AJAX (مخفف Asynchronous JavaScript and XML) که با شیء XMLHttpRequest
پیادهسازی میشود و Fetch API که رویکردی مدرنتر و مبتنی بر Promise را ارائه میدهد.
هدف این مقاله، ارائه یک راهنمای تخصصی و جامع برای توسعهدهندگانی است که به دنبال درک عمیق، مقایسه، و بهکارگیری بهینه این دو فناوری در پروژههای خود هستند. ما نه تنها به مبانی و نحوه استفاده از هر یک خواهیم پرداخت، بلکه الگوهای پیشرفته، بهترین روشها، مدیریت خطا، و ملاحظات امنیتی را نیز پوشش خواهیم داد تا شما را در ساخت اپلیکیشنهای وب قدرتمند و مقیاسپذیر یاری کنیم.
درک مبانی APIها و پروتکل HTTP
پیش از ورود به جزئیات AJAX و Fetch، لازم است که فهم دقیقی از مفهوم API و پروتکل HTTP، که شالوده ارتباطات وب را تشکیل میدهد، داشته باشیم.
API چیست؟
API به مجموعهای از توابع و روالها گفته میشود که به برنامههای کاربردی اجازه میدهد تا با یکدیگر ارتباط برقرار کنند. در زمینه وب، APIها معمولاً به سرویسهایی اشاره دارند که از طریق پروتکل HTTP در دسترس قرار میگیرند و به برنامههای کلاینت (مانند مرورگرها یا اپلیکیشنهای موبایل) اجازه میدهند تا به دادهها و قابلیتهای یک سرور دسترسی پیدا کنند. رایجترین نوع APIهای وب، RESTful APIها هستند که بر اساس اصول REST (Representational State Transfer) طراحی شدهاند و از متدهای استاندارد HTTP برای انجام عملیات مختلف (مانند بازیابی، ایجاد، بهروزرسانی، و حذف منابع) استفاده میکنند. ساختار داده در این APIها معمولاً به صورت JSON یا XML تبادل میشود.
پروتکل HTTP/HTTPS: زبان وب
HTTP (Hypertext Transfer Protocol) پروتکلی است که برای انتقال دادهها در وب استفاده میشود. HTTPS نسخه امن HTTP است که از رمزنگاری SSL/TLS برای حفاظت از دادهها در حین انتقال استفاده میکند. هر تعامل HTTP شامل یک درخواست (Request) از سمت کلاینت و یک پاسخ (Response) از سمت سرور است. اجزای اصلی یک درخواست و پاسخ HTTP عبارتند از:
- متدهای HTTP (HTTP Methods/Verbs): این متدها نوع عملیاتی را که کلاینت میخواهد روی یک منبع انجام دهد، مشخص میکنند. رایجترین متدها عبارتند از:
GET
: برای بازیابی (خواندن) یک منبع از سرور.POST
: برای ایجاد (ارسال) یک منبع جدید به سرور.PUT
: برای بهروزرسانی (جایگزینی کامل) یک منبع موجود.DELETE
: برای حذف یک منبع.PATCH
: برای بهروزرسانی جزئی یک منبع.HEAD
: مشابه GET است اما فقط هدرها را برمیگرداند، بدون بدنه پاسخ.OPTIONS
: برای دریافت اطلاعات درباره متدهای HTTP پشتیبانی شده توسط سرور برای یک URL خاص.
- آدرس URL: مکان منبع مورد نظر را روی سرور مشخص میکند.
- هدرها (Headers): اطلاعات متادیتا درباره درخواست یا پاسخ را حمل میکنند، مانند نوع محتوا (
Content-Type
)، طول محتوا (Content-Length
)، کوکیها، توکنهای احراز هویت (Authorization
) و غیره. - بدنه (Body): شامل دادههای واقعی است که در درخواستهای
POST
،PUT
وPATCH
ارسال میشوند، یا دادههایی که در پاسخهایGET
برگردانده میشوند (مثلاً یک شیء JSON).
کدهای وضعیت HTTP (HTTP Status Codes): سرور با یک کد وضعیت عددی به درخواست کلاینت پاسخ میدهد که نشاندهنده نتیجه عملیات است. دستههای اصلی کدها عبارتند از:
1xx
: اطلاعاتی (Informational)2xx
: موفقیت (Success) – مثال:200 OK
،201 Created
،204 No Content
3xx
: تغییر مسیر (Redirection) – مثال:301 Moved Permanently
4xx
: خطاهای کلاینت (Client Error) – مثال:400 Bad Request
،401 Unauthorized
،403 Forbidden
،404 Not Found
5xx
: خطاهای سرور (Server Error) – مثال:500 Internal Server Error
,503 Service Unavailable
AJAX (Asynchronous JavaScript and XML): پیشگام ارتباطات ناهمزمان
پیش از ظهور AJAX، برای بهروزرسانی محتوای یک صفحه وب، نیاز به بارگذاری مجدد کامل صفحه بود. این امر تجربه کاربری را مختل میکرد و باعث کندی میشد. در سال 2005، مفهوم AJAX معرفی شد و انقلابی در توسعه وب ایجاد کرد. AJAX به توسعهدهندگان این امکان را داد که بخشهایی از یک صفحه را بدون نیاز به بارگذاری مجدد کل صفحه، بهروزرسانی کنند. این کار با استفاده از شیء XMLHttpRequest
(XHR) که در جاوا اسکریپت مرورگرها تعبیه شده است، انجام میشود.
شیء XMLHttpRequest
XMLHttpRequest
یک شیء API مرورگر است که امکان ارسال درخواستهای HTTP به سرور و دریافت پاسخهای ناهمزمان را فراهم میکند. نام آن تاریخی است؛ در ابتدا عمدتاً برای تبادل دادههای XML استفاده میشد، اما امروزه بیشتر با JSON کار میکند.
نحوه استفاده پایه از XMLHttpRequest
برای انجام یک درخواست GET با XMLHttpRequest
:
function fetchDataWithXHR() {
const xhr = new XMLHttpRequest();
const url = 'https://api.example.com/data';
// 1. تنظیم یک تابع برای رسیدگی به تغییرات وضعیت
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
// 4. درخواست تکمیل شد
if (xhr.status >= 200 && xhr.status < 300) {
// 5. درخواست موفق بود
try {
const data = JSON.parse(xhr.responseText);
console.log('Data received with XHR (GET):', data);
// اینجا میتوانید دادهها را در DOM نمایش دهید
} catch (e) {
console.error('Error parsing JSON:', e);
}
} else {
// 6. درخواست با خطا مواجه شد
console.error('XHR request failed with status:', xhr.status, xhr.statusText);
console.error('Response text:', xhr.responseText);
}
}
};
// 2. باز کردن یک درخواست
// متد GET، URL و true برای ناهمزمان بودن
xhr.open('GET', url, true);
// 3. تنظیم هدرها (اختیاری)
// xhr.setRequestHeader('Content-Type', 'application/json');
// xhr.setRequestHeader('Authorization', 'Bearer YOUR_TOKEN');
// 4. ارسال درخواست
xhr.send();
console.log('XHR GET request sent...');
}
// فراخوانی تابع
// fetchDataWithXHR();
توضیحات مراحل:
new XMLHttpRequest()
: یک شیء جدید XHR ایجاد میکند.xhr.onreadystatechange
: این یک رویدادhandler است که هر زمان که ویژگیreadyState
شیء XHR تغییر کند، فراخوانی میشود.xhr.readyState
: این ویژگی وضعیت فعلی درخواست را نشان میدهد. مقادیر آن عبارتند از:0
(UNSENT): درخواست هنوز باز نشده است.1
(OPENED): متدopen()
فراخوانی شده است.2
(HEADERS_RECEIVED): متدsend()
فراخوانی شده و هدرها و وضعیت در دسترس هستند.3
(LOADING): در حال دانلودresponseText
است؛responseText
تا حدی در دسترس است.4
(DONE): عملیات کامل شده است.
ما معمولاً فقط به
XMLHttpRequest.DONE
(یا4
) اهمیت میدهیم تا مطمئن شویم درخواست به پایان رسیده است.xhr.open(method, url, async)
: این متد برای مقداردهی اولیه یک درخواست استفاده میشود.method
: متد HTTP مانند 'GET' یا 'POST'.url
: آدرس URL برای ارسال درخواست.async
: یک مقدار بولین که مشخص میکند درخواست باید ناهمزمان باشد (true
) یا همزمان (false
). تقریباً همیشه بایدtrue
باشد تا UI مسدود نشود.
xhr.setRequestHeader(header, value)
: برای تنظیم هدرهای درخواست، مانندContent-Type
یاAuthorization
، استفاده میشود. باید پس ازopen()
و قبل ازsend()
فراخوانی شود.xhr.send(body)
: درخواست را به سرور ارسال میکند. برای درخواستهایGET
یاHEAD
، آرگومانbody
بایدnull
باشد. برایPOST
یاPUT
، شامل دادههایی است که باید ارسال شوند.xhr.status
: کد وضعیت HTTP پاسخ (مثلاً 200 برای موفقیت، 404 برای یافت نشد، 500 برای خطای سرور).xhr.responseText
: محتوای پاسخ سرور به صورت یک رشته متنی. اگر پاسخ JSON باشد، باید آن را باJSON.parse()
تبدیل کنید.xhr.responseXML
: محتوای پاسخ سرور به صورت یک شیء XML DOM (در صورتی که نوع محتوا XML باشد).
ارسال داده با POST توسط XMLHttpRequest
function postDataWithXHR() {
const xhr = new XMLHttpRequest();
const url = 'https://api.example.com/posts';
const dataToSend = {
title: 'My New Post',
body: 'This is the content of my new post.',
userId: 1
};
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status >= 200 && xhr.status < 300) {
try {
const responseData = JSON.parse(xhr.responseText);
console.log('Post created with XHR:', responseData);
} catch (e) {
console.error('Error parsing JSON:', e);
}
} else {
console.error('XHR POST request failed:', xhr.status, xhr.statusText);
}
}
};
xhr.open('POST', url, true);
// بسیار مهم: تنظیم Content-Type برای ارسال JSON
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(dataToSend)); // تبدیل شیء جاوا اسکریپت به رشته JSON
console.log('XHR POST request sent...');
}
// postDataWithXHR();
مزایا و معایب XMLHttpRequest
مزایا:
- پشتیبانی گسترده مرورگرها (سازگاری بالا حتی با مرورگرهای قدیمی).
- کنترل دقیق بر درخواستها (هدرها، Progress events و غیره).
معایب:
- API قدیمی و verbose (پرگویی): نوشتن کدهای آن میتواند طولانی و پیچیده باشد، به خصوص برای درخواستهای پیچیده یا زنجیرهای.
- مدیریت callback hell: با افزایش تعداد درخواستهای متوالی و وابسته به هم، کدهای callback میتوانند به سرعت نامرتب و دشوار برای نگهداری شوند.
- عدم پشتیبانی مستقیم از Promises: نیاز به بستهبندی دستی در Promiseها برای استفاده از
async/await
. - اینترفیس غیرطبیعی: طراحی آن به اندازه Fetch API مدرن و JavaScript-friendly نیست.
ورود Fetch API: نسل جدید درخواستهای وب
Fetch API، که در ES6 (ECMAScript 2015) معرفی شد، یک جایگزین مدرن و قدرتمند برای XMLHttpRequest
است. این API مبتنی بر Promise است و یک اینترفیس تمیزتر و منعطفتر برای ساخت درخواستهای HTTP ارائه میدهد. Fetch API به طور ذاتی با ویژگیهای جدید جاوا اسکریپت مانند async/await
سازگاری دارد که مدیریت کد ناهمزمان را بسیار سادهتر میکند.
مقایسه Fetch با XMLHttpRequest
- مبتنی بر Promise: Fetch به طور طبیعی Promiseها را برمیگرداند که اجازه میدهد از
.then()
و.catch()
برای مدیریت موفقیت و شکست استفاده کرد و باasync/await
کار کند. XHR مبتنی بر event handlerها و callbackها است. - سینتکس تمیزتر: کد Fetch به طور قابل توجهی کوتاهتر و خواناتر از XHR است.
- پشتیبانی از Stream: Fetch میتواند با استریمهای داده کار کند که برای حجمهای بزرگ داده کارآمدتر است.
- پیشفرض No-CORS: Fetch به صورت پیشفرض با حالت
no-cors
کار میکند که میتواند برای درخواست به دامنههای دیگر مشکلساز باشد مگر اینکه سرور هدرهای CORS مناسب را ارسال کند. XHR این محدودیت را ندارد مگر اینکه به طور صریح غیرفعال شود. - مدیریت خطا: Fetch تنها زمانی Promise را reject میکند که یک خطای شبکه رخ دهد (مانند عدم دسترسی به اینترنت). کدهای وضعیت HTTP مانند 404 یا 500 منجر به reject شدن Promise نمیشوند؛ در این حالت Promise resolve میشود اما با یک پاسخ که ویژگی
response.ok
آنfalse
است. این یک تفاوت مهم با XHR است که در صورت دریافت کد وضعیت 4xx/5xx، خطای XHR را نشان میدهد.
نحوه استفاده پایه از Fetch API
یک درخواست GET ساده با Fetch:
function fetchDataWithFetch() {
const url = 'https://api.example.com/data';
fetch(url)
.then(response => {
// بررسی وضعیت HTTP
if (!response.ok) {
// اگر کد وضعیت 2xx نباشد، خطا پرتاب کن
throw new Error(`HTTP error! status: ${response.status}`);
}
// تبدیل پاسخ به JSON
return response.json();
})
.then(data => {
console.log('Data received with Fetch (GET):', data);
// اینجا میتوانید دادهها را در DOM نمایش دهید
})
.catch(error => {
// مدیریت خطاهای شبکه یا خطاهای پرتاب شده در .then()
console.error('Fetch GET request failed:', error);
});
console.log('Fetch GET request sent...');
}
// فراخوانی تابع
// fetchDataWithFetch();
در مثال بالا، fetch(url)
یک Promise را برمیگرداند. اولین .then()
با شیء Response
حل میشود. ما ابتدا response.ok
را بررسی میکنیم تا مطمئن شویم درخواست HTTP موفقیتآمیز بوده است (کد وضعیت 200-299). سپس response.json()
را فراخوانی میکنیم که خود یک Promise دیگر را برمیگرداند که محتوای بدنه پاسخ را به صورت یک شیء جاوا اسکریپت تجزیه میکند. .catch()
برای گرفتن خطاهای شبکه (مانند قطع شدن اینترنت) یا هر خطایی که در داخل .then()
ها پرتاب شود، استفاده میشود.
ارسال داده با POST توسط Fetch API
function postDataWithFetch() {
const url = 'https://api.example.com/posts';
const dataToSend = {
title: 'My New Post with Fetch',
body: 'This is the content of my new post using Fetch API.',
userId: 1
};
fetch(url, {
method: 'POST', // متد HTTP
headers: {
'Content-Type': 'application/json', // نوع محتوای ارسالی
'Authorization': 'Bearer YOUR_TOKEN_HERE' // مثال: هدر احراز هویت
},
body: JSON.stringify(dataToSend) // تبدیل شیء جاوا اسکریپت به رشته JSON
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Post created with Fetch:', data);
})
.catch(error => {
console.error('Fetch POST request failed:', error);
});
console.log('Fetch POST request sent...');
}
// postDataWithFetch();
برای درخواستهای POST
، PUT
، DELETE
یا PATCH
، آرگومان دوم fetch()
یک شیء پیکربندی است که شامل موارد زیر است:
method
: متد HTTP مورد نظر.headers
: یک شیء شامل هدرهای درخواست.body
: دادههایی که باید ارسال شوند. برای JSON، باید ازJSON.stringify()
استفاده شود.
شیء Request و Response
Fetch API بر اساس مفاهیم اصلی شیء Request
و Response
ساخته شده است. این اشیاء نمایانگر بخشهای پروتکل HTTP هستند:
Request
: یک شیءRequest
نمایانگر یک درخواست HTTP است. شما میتوانید یک شیءRequest
را به صورت دستی ایجاد کنید و آن را بهfetch()
ارسال کنید تا کنترل دقیقتری بر درخواست داشته باشید.const myRequest = new Request('https://api.example.com/data', { method: 'GET', headers: { 'Accept': 'application/json' }, cache: 'no-store' // مثال: جلوگیری از کش شدن }); fetch(myRequest) .then(response => response.json()) .then(data => console.log('Data from Request object:', data)) .catch(error => console.error('Error with Request object:', error));
Response
: یک شیءResponse
نمایانگر پاسخ به یک درخواست HTTP است. این شیء حاوی اطلاعاتی مانند کد وضعیت (status
)، متن وضعیت (statusText
)، هدرها (headers
) و متدهای مختلفی برای دسترسی به بدنه پاسخ (json()
,text()
,blob()
و غیره) است.
لغو درخواستها با AbortController
یکی از قابلیتهای مهم در مدیریت درخواستهای شبکه، امکان لغو (Abort) یک درخواست در حال انجام است. این امر به خصوص در سناریوهایی که کاربر درخواست جدیدی را قبل از تکمیل درخواست قبلی ارسال میکند (مانند جستجوهای تایپ همزمان)، مفید است. Fetch API از AbortController
برای این منظور استفاده میکند:
let controller; // کنترلر را در یک دامنه بالاتر تعریف کنید تا بتوانید آن را در هر زمان لغو کنید
function cancelableFetch() {
// اگر درخواست قبلی در حال انجام بود، آن را لغو کنید
if (controller) {
controller.abort();
console.log('Previous request aborted.');
}
controller = new AbortController();
const signal = controller.signal; // سیگنال برای AbortController
const url = 'https://api.example.com/long-running-task';
fetch(url, { signal }) // ارسال سیگنال به fetch
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Data received:', data);
controller = null; // درخواست با موفقیت تکمیل شد
})
.catch(error => {
if (error.name === 'AbortError') {
console.warn('Fetch request was aborted.');
} else {
console.error('Fetch error:', error);
}
controller = null; // درخواست با خطا یا لغو به پایان رسید
});
console.log('New fetch request started...');
}
// برای مثال، هر 2 ثانیه یکبار فراخوانی کنید
// setInterval(cancelableFetch, 2000);
// برای لغو دستی:
// setTimeout(() => {
// if (controller) {
// controller.abort();
// console.log('Manually aborted the request!');
// }
// }, 1000);
مدیریت خطا و حالات بارگذاری در API Calls
مدیریت صحیح خطاها و بازخورد به کاربر درباره وضعیت بارگذاری دادهها، بخش حیاتی از توسعه اپلیکیشنهای وب کاربرپسند و مقاوم است.
استراتژیهای مدیریت خطا
- خطاهای شبکه در مقابل خطاهای HTTP:
- خطاهای شبکه (Network Errors): این خطاها زمانی رخ میدهند که درخواست اصلاً به سرور نمیرسد یا اتصال قطع میشود (مانند عدم اتصال به اینترنت، مشکلات DNS). Fetch Promise را
reject
میکند. XHR نیز یک خطای'error'
را fire میکند. این نوع خطاها معمولاً با پیامهایی مانند "Failed to fetch" یا "Network Error" نمایش داده میشوند. - خطاهای HTTP (HTTP Errors): این خطاها زمانی رخ میدهند که درخواست به سرور میرسد اما سرور با کد وضعیت خطایی (مانند 4xx یا 5xx) پاسخ میدهد. Fetch Promise را
resolve
میکند (زیرا درخواست شبکه موفق بوده است)، اما ویژگیresponse.ok
رویfalse
تنظیم میشود. بنابراین، شما باید صراحتاًresponse.ok
را بررسی کنید و در صورت نیاز یک خطاthrow
کنید تا به بلاک.catch()
برسد. XHR در این حالت وضعیتstatus
را برمیگرداند وonreadystatechange
را فراخوانی میکند.
- خطاهای شبکه (Network Errors): این خطاها زمانی رخ میدهند که درخواست اصلاً به سرور نمیرسد یا اتصال قطع میشود (مانند عدم اتصال به اینترنت، مشکلات DNS). Fetch Promise را
- استفاده از
try...catch
باasync/await
:async/await
کد ناهمزمان را به گونهای مینویسد که شبیه کد همزمان به نظر برسد و مدیریت خطا را باtry...catch
بسیار ساده میکند:async function fetchDataAsync() { const url = 'https://api.example.com/data'; try { const response = await fetch(url); if (!response.ok) { const errorBody = await response.text(); // یا response.json() throw new Error(`HTTP error! Status: ${response.status}. Message: ${errorBody}`); } const data = await response.json(); console.log('Data received (async/await):', data); // نمایش داده } catch (error) { console.error('Failed to fetch data (async/await):', error); // نمایش پیام خطا به کاربر if (error.name === 'TypeError' || error.message.includes('Failed to fetch')) { console.error('Network error or CORS issue'); } else if (error.message.includes('HTTP error!')) { console.error('Server returned an error status:', error.message); } // میتوانید بسته به نوع خطا پیامهای مختلفی نشان دهید } } // fetchDataAsync();
استفاده از
async/await
به شدت توصیه میشود زیرا خوانایی کد را به طرز چشمگیری بهبود میبخشد و خطاهای پرتاب شده را به صورت معمول، در بلاکcatch
مدیریت میکند، بدون نیاز به زنجیرههای.then().catch()
طولانی. - اطلاعات خطا از سرور: در بسیاری از APIها، سرور در صورت بروز خطا (مثلاً 400 Bad Request یا 404 Not Found)، یک شیء JSON در بدنه پاسخ ارسال میکند که حاوی جزئیات بیشتری درباره خطا است. همیشه سعی کنید این اطلاعات را از
response.json()
یاresponse.text()
بخوانید تا پیام خطای معنیدارتری به کاربر نمایش دهید.
نمایش حالات بارگذاری و بازخورد به کاربر
هنگامی که دادهها از سرور واکشی میشوند، ممکن است تأخیری وجود داشته باشد. برای بهبود تجربه کاربری، باید بازخورد بصری مناسبی به کاربر ارائه دهید:
- اسپینرها یا لودینگ ایندیکیتورها: قبل از شروع درخواست، یک اسپینر یا پیام "در حال بارگذاری..." را نمایش دهید و پس از دریافت پاسخ (چه موفق و چه ناموفق)، آن را پنهان کنید.
- Skeleton Screens: به جای اسپینر، میتوانید از Skeleton Screens استفاده کنید که طرح کلی محتوای در حال بارگذاری را نشان میدهند و تجربه بصری روانتری را ارائه میدهند.
- غیرفعال کردن دکمهها: در حین ارسال داده (مثلاً هنگام ارسال فرم)، دکمه ارسال را غیرفعال کنید تا از ارسال چندباره جلوگیری شود و کاربر متوجه شود که عملیات در حال انجام است.
- پیامهای خطا و موفقیت: پس از اتمام درخواست، یک پیام مختصر و واضح (مثلاً در یک توست یا نوار اعلان) درباره موفقیت یا شکست عملیات نمایش دهید.
<div id="loadingIndicator" style="display: none;">در حال بارگذاری...</div>
<button id="fetchButton">واکشی داده</button>
<div id="dataContainer"></div>
<div id="errorMessage" style="color: red;"></div>
<script>
const loadingIndicator = document.getElementById('loadingIndicator');
const fetchButton = document.getElementById('fetchButton');
const dataContainer = document.getElementById('dataContainer');
const errorMessage = document.getElementById('errorMessage');
async function handleFetchClick() {
loadingIndicator.style.display = 'block';
fetchButton.disabled = true;
dataContainer.innerHTML = '';
errorMessage.innerHTML = '';
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
const errorText = await response.text();
throw new Error(`خطای HTTP! وضعیت: ${response.status}. ${errorText}`);
}
const data = await response.json();
dataContainer.innerHTML = '<p>دادهها با موفقیت دریافت شدند:</p><pre><code>' + JSON.stringify(data, null, 2) + '</code></pre>';
} catch (error) {
errorMessage.innerHTML = '<p>خطا در واکشی دادهها: ' + error.message + '</p>';
console.error('Fetch error:', error);
} finally {
loadingIndicator.style.display = 'none';
fetchButton.disabled = false;
}
}
fetchButton.addEventListener('click', handleFetchClick);
</script>
مدیریت Race Conditions (شرایط رقابت)
Race condition زمانی اتفاق میافتد که ترتیب پاسخدهی چندین درخواست ناهمزمان، غیرقابل پیشبینی باشد و ممکن است منجر به نمایش دادههای نادرست شود. به عنوان مثال، اگر کاربر به سرعت دو بار روی دکمه "جستجو" کلیک کند، ممکن است پاسخ درخواست دوم زودتر از پاسخ درخواست اول برسد و نتیجه جستجوی اول را به اشتباه نمایش دهد.
راهحلها:
- لغو درخواستهای قبلی (AbortController): همانطور که قبلاً نشان داده شد، قبل از شروع درخواست جدید، درخواستهای قبلی را لغو کنید.
- Debouncing/Throttling: از توابع Debounce یا Throttle برای محدود کردن تعداد فراخوانیهای تابع ارسال درخواست در یک بازه زمانی مشخص استفاده کنید.
- نادیده گرفتن پاسخهای قدیمی: یک counter یا timestamp را با هر درخواست ارسال کنید و تنها در صورتی پاسخ را پردازش کنید که مربوط به آخرین درخواست ارسال شده باشد.
الگوهای پیشرفته و بهترین روشها
Async/Await: مدیریت سادهتر Promiseها
async/await
یک سینتکس ویژه در جاوا اسکریپت است که کار با Promiseها را بسیار سادهتر و کد ناهمزمان را شبیه کد همزمان میکند. یک تابع async
همیشه یک Promise برمیگرداند و کلمه کلیدی await
میتواند فقط در داخل یک تابع async
استفاده شود و اجرای تابع را متوقف میکند تا Promise مورد انتظار resolve
شود.
مثال کاملتر با async/await
برای GET و POST:
async function fetchAndPostExample() {
const baseUrl = 'https://jsonplaceholder.typicode.com'; // یک API تستی رایگان
// === GET Request ===
try {
console.log('Fetching posts...');
const postsResponse = await fetch(`${baseUrl}/posts?_limit=5`); // درخواست 5 پست اول
if (!postsResponse.ok) {
throw new Error(`HTTP error! status: ${postsResponse.status}`);
}
const posts = await postsResponse.json();
console.log('Fetched Posts:', posts);
} catch (error) {
console.error('Error fetching posts:', error);
}
// === POST Request ===
const newPost = {
title: 'New Post Title from Fetch/Async',
body: 'This is the body of the new post created with async/await and Fetch.',
userId: 1
};
try {
console.log('Creating a new post...');
const postResponse = await fetch(`${baseUrl}/posts`, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8'
},
body: JSON.stringify(newPost)
});
if (!postResponse.ok) {
throw new Error(`HTTP error! status: ${postResponse.status}`);
}
const createdPost = await postResponse.json();
console.log('Created Post:', createdPost);
} catch (error) {
console.error('Error creating post:', error);
}
}
// fetchAndPostExample();
سریالسازی و دسیریالسازی دادهها (JSON)
در وب مدرن، JSON (JavaScript Object Notation) رایجترین فرمت برای تبادل دادهها بین کلاینت و سرور است. جاوا اسکریپت توابع داخلی برای کار با JSON دارد:
JSON.stringify(object)
: یک شیء یا مقدار جاوا اسکریپت را به یک رشته JSON تبدیل میکند. این برای ارسال دادهها به سرور در بدنه درخواست (body
) استفاده میشود.JSON.parse(jsonString)
: یک رشته JSON را تجزیه کرده و یک شیء یا مقدار جاوا اسکریپت را برمیگرداند. این برای تبدیل پاسخ سرور به یک شیء قابل استفاده در جاوا اسکریپت استفاده میشود. (Fetch API باresponse.json()
این کار را به صورت خودکار انجام میدهد.)
احراز هویت (Authentication)
دسترسی به بسیاری از APIها نیاز به احراز هویت دارد تا سرور هویت کلاینت را شناسایی و مجوزهای دسترسی آن را بررسی کند. روشهای رایج احراز هویت در APIهای وب شامل:
- API Keys: یک رشته منحصر به فرد که در هدر یا به عنوان یک پارامتر query در URL ارسال میشود. (کمتر امن)
- توکنهای مبتنی بر JWT (JSON Web Tokens): پس از ورود کاربر، سرور یک JWT صادر میکند. این توکن در هر درخواست بعدی در هدر
Authorization
به صورتBearer YOUR_TOKEN
ارسال میشود. این رایجترین روش در SPAها است. - OAuth 2.0: یک فریمورک احراز هویت برای دسترسی امن و محدود به منابع کاربر بدون نیاز به افشای اطلاعات ورود کاربر. این معمولاً برای لاگین با شبکههای اجتماعی (مثل Google، Facebook) یا اعطای دسترسی به برنامههای شخص ثالث استفاده میشود.
مثال ارسال توکن JWT در هدر:
async function fetchWithAuth() {
const token = 'YOUR_JWT_TOKEN_HERE'; // توکن JWT دریافت شده پس از ورود
const url = 'https://api.example.com/protected-data';
try {
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`, // ارسال توکن در هدر Authorization
'Content-Type': 'application/json'
}
});
if (response.status === 401) {
console.error('Unauthorized: Invalid or expired token');
// ریدایرکت به صفحه ورود یا نمایش پیام
return;
}
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('Protected Data:', data);
} catch (error) {
console.error('Error fetching protected data:', error);
}
}
// fetchWithAuth();
کشینگ (Caching)
کشینگ به ذخیره پاسخهای API در کلاینت (مرورگر) برای کاهش تعداد درخواستهای شبکه و بهبود عملکرد کمک میکند. روشهای مختلفی برای مدیریت کشینگ وجود دارد:
- کشینگ HTTP (Headers): سرور میتواند هدرهایی مانند
Cache-Control
،Expires
وETag
را ارسال کند که به مرورگر میگوید چگونه پاسخ را کش کند.Cache-Control: public, max-age=3600
: به مرورگر میگوید پاسخ را برای یک ساعت کش کند.ETag
: یک شناسه منحصر به فرد برای نسخه فعلی منبع. اگر مرورگر یکETag
ذخیره شده داشته باشد، میتواند در درخواست بعدی هدرIf-None-Match
را ارسال کند. سرور اگر منبع تغییر نکرده باشد، با304 Not Modified
پاسخ میدهد.
- کشینگ سمت کلاینت (جاوا اسکریپت): میتوانید دادهها را در
localStorage
،sessionStorage
یاIndexedDB
ذخیره کنید.async function fetchDataWithClientCache(url, cacheKey) { const cachedData = localStorage.getItem(cacheKey); if (cachedData) { console.log('Data fetched from cache:', JSON.parse(cachedData)); // بررسی تاریخ انقضا و بهروزرسانی در پسزمینه // return JSON.parse(cachedData); } try { const response = await fetch(url); if (!response.ok) throw new Error('Network response was not ok.'); const data = await response.json(); localStorage.setItem(cacheKey, JSON.stringify(data)); // ذخیره در کش console.log('Data fetched from network and cached:', data); return data; } catch (error) { console.error('Error fetching data:', error); if (cachedData) { return JSON.parse(cachedData); // در صورت خطا، داده کش شده را برگردان } throw error; } } // fetchDataWithClientCache('https://api.example.com/products', 'productsCache');
- Service Workers: برای کنترل پیشرفتهتر کشینگ و ارائه قابلیتهای آفلاین.
محدودیت نرخ (Rate Limiting)
بسیاری از APIها محدودیتی در تعداد درخواستهایی که میتوانید در یک بازه زمانی مشخص ارسال کنید، اعمال میکنند. اگر از این محدودیت فراتر روید، سرور معمولاً با کد وضعیت 429 Too Many Requests
پاسخ میدهد و ممکن است هدر Retry-After
را نیز ارسال کند که نشان میدهد چه زمانی میتوانید درخواست بعدی را ارسال کنید. باید کدهای خود را طوری طراحی کنید که این پاسخها را شناسایی کرده و به صورت مناسب مدیریت کنند (مثلاً با مکانیزم "exponential backoff" که درخواستها را با تاخیر فزایندهای دوباره ارسال میکند).
ملاحظات امنیتی
- Cross-Site Scripting (XSS): هرگز دادههای دریافت شده از API را مستقیماً بدون sanitization در DOM قرار ندهید. همیشه دادهها را پیش از نمایش از نظر اسکریپتهای مخرب بررسی و پاکسازی کنید.
- Cross-Site Request Forgery (CSRF): اطمینان حاصل کنید که درخواستهای
POST
،PUT
وDELETE
شما شامل توکنهای CSRF باشند (که سرور تولید میکند و باید در هر درخواست ارسال شود) تا از حملات CSRF جلوگیری شود. - HTTPS: همیشه از HTTPS برای تمام ارتباطات API استفاده کنید تا دادهها در حین انتقال رمزنگاری شوند.
- پنهان نگه داشتن اعتبارنامههای حساس: کلیدهای API حساس یا اعتبارنامهها را در کد سمت کلاینت ذخیره نکنید. آنها را در سمت سرور مدیریت کنید.
طراحی توابع/ماژولهای کلاینت API قابل استفاده مجدد
برای مدیریت بهتر کد و افزایش قابلیت استفاده مجدد، بهتر است یک ماژول یا کلاس جداگانه برای تعامل با APIهای خود ایجاد کنید. این کار به شما امکان میدهد:
- مرکزیسازی URLهای پایه: تمام URLهای API را در یک مکان تعریف کنید.
- مدیریت هدرهای مشترک: هدرهایی مانند
Content-Type
یاAuthorization
را به صورت خودکار به تمام درخواستها اضافه کنید. - مدیریت خطای مرکزی: یک منطق مدیریت خطا برای تمام درخواستها پیادهسازی کنید.
class ApiClient {
constructor(baseUrl, defaultHeaders = {}) {
this.baseUrl = baseUrl;
this.defaultHeaders = {
'Content-Type': 'application/json',
...defaultHeaders
};
}
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const config = {
...options,
headers: {
...this.defaultHeaders,
...options.headers
}
};
try {
const response = await fetch(url, config);
if (!response.ok) {
let errorDetails = `HTTP error! Status: ${response.status}`;
try {
const errorJson = await response.json();
errorDetails += ` - ${errorJson.message || JSON.stringify(errorJson)}`;
} catch (e) {
// اگر پاسخ JSON نبود
errorDetails += ` - ${await response.text()}`;
}
throw new Error(errorDetails);
}
// اگر پاسخ NoContent باشد (204)
if (response.status === 204) {
return null;
}
return response.json();
} catch (error) {
console.error('API Client Error:', error);
throw error; // خطا را دوباره پرتاب کن تا توسط کدهای فراخوانی کننده مدیریت شود
}
}
get(endpoint, params = {}) {
const queryString = new URLSearchParams(params).toString();
const fullEndpoint = queryString ? `${endpoint}?${queryString}` : endpoint;
return this.request(fullEndpoint, { method: 'GET' });
}
post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
put(endpoint, data) {
return this.request(endpoint, {
method: 'PUT',
body: JSON.stringify(data)
});
}
delete(endpoint) {
return this.request(endpoint, { method: 'DELETE' });
}
setAuthToken(token) {
this.defaultHeaders['Authorization'] = `Bearer ${token}`;
}
removeAuthToken() {
delete this.defaultHeaders['Authorization'];
}
}
// مثال استفاده:
// const myApiClient = new ApiClient('https://api.example.com');
// myApiClient.setAuthToken('your_jwt_token_here');
// async function testApiClient() {
// try {
// const users = await myApiClient.get('/users', { limit: 10 });
// console.log('Users:', users);
// const newUser = await myApiClient.post('/users', { name: 'Ali', email: 'ali@example.com' });
// console.log('New User:', newUser);
// await myApiClient.delete('/users/123'); // فرض کنید id کاربر 123 است
// console.log('User 123 deleted.');
// } catch (error) {
// console.error('Error in API client usage:', error);
// }
// }
// testApiClient();
ابزارها و کتابخانههای کمکی
در حالی که Fetch API ابزاری قدرتمند است، کتابخانههای جانبی نیز برای سادهسازی بیشتر کار با APIها و ارائه قابلیتهای اضافی توسعه یافتهاند.
Axios: یک جایگزین محبوب برای Fetch
Axios یک کلاینت HTTP مبتنی بر Promise برای مرورگر و Node.js است. در بین توسعهدهندگان بسیار محبوب است و بسیاری از ویژگیهایی را ارائه میدهد که Fetch API به صورت Native ندارد یا پیادهسازی آنها پیچیدهتر است.
مزایای Axios نسبت به Fetch API:
- پشتیبانی از مرورگرهای قدیمیتر: Axios به صورت خودکار از
XMLHttpRequest
برای مرورگرهای قدیمیتر که Fetch API را پشتیبانی نمیکنند، استفاده میکند. - قابلیت لغو درخواستها (Cancellations): دارای API داخلی برای لغو درخواستها (برخلاف Fetch که نیاز به
AbortController
جداگانه دارد). - اینترسپتورها (Interceptors): اینترسپتورها به شما اجازه میدهند تا درخواستها یا پاسخها را قبل از اینکه توسط
.then()
یا.catch()
مدیریت شوند، رهگیری و تغییر دهید. این برای افزودن هدرهای احراز هویت به تمام درخواستها، لاگ کردن درخواستها، یا مدیریت خطاهای عمومی (مانند ریدایرکت هنگام 401) بسیار مفید است. - تبدیل خودکار JSON: Axios به طور خودکار دادههای JSON را در درخواستها (serializes) و پاسخها (deserializes) تبدیل میکند، بنابراین نیازی به
JSON.stringify()
یاresponse.json()
نیست. - مدیریت خطا بهتر: Axios در صورت دریافت کدهای وضعیت 4xx/5xx، Promise را
reject
میکند که با نحوه مدیریت خطاهای سنتی جاوا اسکریپت سازگارتر است. - گزارش پیشرفت (Progress Reporting): امکان دسترسی به Progress events برای آپلود و دانلود.
- امنیت (XSRF Protection): به صورت پیشفرض از Cross-Site Request Forgery (XSRF) محافظت میکند.
مثال Axios:
// نیاز به نصب: npm install axios یا yarn add axios
// import axios from 'axios';
function fetchDataWithAxios() {
axios.get('https://api.example.com/data')
.then(response => {
// دادهها به صورت خودکار JSON.parsed شدهاند
console.log('Data received with Axios (GET):', response.data);
console.log('Status:', response.status);
console.log('Headers:', response.headers);
})
.catch(error => {
if (error.response) {
// سرور پاسخ داد اما با کد وضعیت خارج از محدوده 2xx
console.error('Axios error response:', error.response.data);
console.error('Axios error status:', error.response.status);
console.error('Axios error headers:', error.response.headers);
} else if (error.request) {
// درخواست ارسال شد اما پاسخی دریافت نشد (خطای شبکه)
console.error('Axios error request (No response received):', error.request);
} else {
// خطاهای دیگری که در تنظیم درخواست رخ دادند
console.error('Axios error message:', error.message);
}
console.error('Axios config:', error.config);
});
}
function postDataWithAxios() {
const dataToSend = {
title: 'Axios Post',
body: 'This post was sent with Axios.',
userId: 1
};
axios.post('https://api.example.com/posts', dataToSend, {
headers: {
'Authorization': 'Bearer YOUR_TOKEN'
}
})
.then(response => {
console.log('Post created with Axios:', response.data);
})
.catch(error => {
console.error('Axios POST error:', error);
});
}
// fetchDataWithAxios();
// postDataWithAxios();
jQuery AJAX (تاریخی)
پیش از ظهور Fetch API و محبوبیت Axios، jQuery AJAX یکی از رایجترین راهها برای انجام درخواستهای ناهمزمان در جاوا اسکریپت بود. این ابزار یک انتزاع سادهتر بر روی XMLHttpRequest
ارائه میداد و پیچیدگیهای آن را پنهان میکرد.
مثال jQuery AJAX (فقط برای شناخت، استفاده آن در پروژههای جدید توصیه نمیشود):
// نیاز به کتابخانه jQuery
// $.ajax({
// url: 'https://api.example.com/data',
// method: 'GET',
// dataType: 'json', // نوع داده مورد انتظار پاسخ
// success: function(data) {
// console.log('Data received with jQuery AJAX:', data);
// },
// error: function(jqXHR, textStatus, errorThrown) {
// console.error('jQuery AJAX error:', textStatus, errorThrown, jqXHR.responseText);
// }
// });
با وجود سادگی، jQuery AJAX نیز از مدل callback استفاده میکرد و با ظهور Promises و Fetch API، استفاده از آن در پروژههای جدید جاوا اسکریپت مدرن کمتر توصیه میشود.
ابزارهای تست API
برای توسعه و تست APIها، ابزارهای مختلفی وجود دارند که به توسعهدهندگان کمک میکنند درخواستها را به سرور ارسال و پاسخها را مشاهده کنند:
- Postman: یک پلتفرم کامل برای طراحی، تست، و مستندسازی APIها. بسیار قدرتمند و پرکاربرد است.
- Insomnia: جایگزینی مشابه Postman با رابط کاربری تمیز و ساده.
- Browser Developer Tools: تب "Network" در ابزارهای توسعهدهنده مرورگر (Chrome DevTools, Firefox Developer Tools) ابزاری ضروری برای مشاهده و اشکالزدایی تمام درخواستهای HTTP است که از صفحه شما ارسال و دریافت میشوند. میتوانید هدرها، بدنه درخواست/پاسخ، وضعیت، زمانبندی و غیره را بررسی کنید.
سناریوهای عملی و مثالهای پیچیدهتر
آپلود فایل با Fetch API و FormData
برای آپلود فایلها، باید از شیء FormData
استفاده کنید که دادهها را در قالب multipart/form-data
کپسوله میکند، درست مانند یک فرم HTML معمولی:
async function uploadFile(file) {
const url = 'https://api.example.com/upload';
const formData = new FormData();
formData.append('profileImage', file, file.name); // 'profileImage' نام فیلد در سمت سرور
try {
const response = await fetch(url, {
method: 'POST',
// نیازی به تنظیم Content-Type نیست؛ Fetch و FormData آن را به درستی تنظیم میکنند
body: formData
});
if (!response.ok) {
throw new Error(`Upload failed! Status: ${response.status}`);
}
const result = await response.json();
console.log('File upload successful:', result);
} catch (error) {
console.error('Error uploading file:', error);
}
}
// مثال استفاده:
// const fileInput = document.createElement('input');
// fileInput.type = 'file';
// document.body.appendChild(fileInput);
// fileInput.addEventListener('change', (event) => {
// const selectedFile = event.target.files[0];
// if (selectedFile) {
// uploadFile(selectedFile);
// }
// });
بروزرسانیهای Real-time (Real-time Updates)
برای سناریوهایی که نیاز به بهروزرسانیهای لحظهای از سرور دارند (مانند چت، نوتیفیکیشنها، داشبوردهای زنده)، درخواستهای HTTP سنتی (AJAX/Fetch) به تنهایی کافی نیستند. در این موارد، از فناوریهایی مانند:
- WebSockets: یک پروتکل ارتباطی دوطرفه و تمامدوطرفه بر بستر TCP که به سرور اجازه میدهد دادهها را به کلاینت push کند و بالعکس، بدون نیاز به درخواستهای مکرر.
- Server-Sent Events (SSE): یک پروتکل یکطرفه که به سرور اجازه میدهد دادهها را به کلاینت push کند. سادهتر از WebSockets است اما فقط برای دریافت داده از سرور مناسب است.
- Polling/Long Polling: Polling شامل ارسال درخواستهای
GET
مکرر در فواصل زمانی ثابت است. Long Polling نیز شامل ارسال درخواست میشود، اما سرور تا زمانی که دادهای برای ارسال نداشته باشد، پاسخ را باز نگه میدارد. این روشها نسبت به WebSockets و SSE ناکارآمدتر هستند اما در برخی موارد ممکن است تنها گزینه باشند.
در حالی که AJAX و Fetch API برای Polling استفاده میشوند، برای راهحلهای Real-time واقعی بهتر است به سراغ WebSockets یا SSE بروید.
صفحهبندی (Pagination)
هنگام کار با حجم زیادی از دادهها، دریافت تمام آنها در یک درخواست ناکارآمد و غیرممکن است. صفحهبندی به شما اجازه میدهد تا دادهها را در "صفحات" یا "قطعات" کوچکتر درخواست کنید. این کار معمولاً با ارسال پارامترهای query مانند page
و limit
(یا offset
) به API انجام میشود.
async function fetchPaginatedData(page = 1, limit = 10) {
const url = `https://api.example.com/items?page=${page}&limit=${limit}`;
try {
const response = await fetch(url);
if (!response.ok) throw new Error('Failed to fetch paginated data.');
const data = await response.json();
console.log(`Page ${page} data:`, data);
// نمایش دادهها و کنترل دکمههای صفحهبندی (بعدی/قبلی)
return data;
} catch (error) {
console.error('Error fetching paginated data:', error);
}
}
// fetchPaginatedData(1, 20); // درخواست صفحه 1 با 20 آیتم
// fetchPaginatedData(2, 20); // درخواست صفحه 2
واکشی همزمان چندین درخواست (Concurrent Requests)
در برخی موارد، ممکن است نیاز باشد چندین درخواست API را به صورت همزمان ارسال کنید و زمانی که تمام آنها پاسخ دادند، نتایج را پردازش کنید. جاوا اسکریپت برای این کار توابعی در Promise
ارائه میدهد:
Promise.all(iterable)
: آرایهای از Promiseها را میگیرد و یک Promise جدید را برمیگرداند. این Promise جدید زمانیresolve
میشود که تمام Promiseهای موجود در آرایهresolve
شوند. اگر حتی یکی از Promiseهاreject
شود،Promise.all
فوراًreject
میشود.Promise.allSettled(iterable)
: آرایهای از Promiseها را میگیرد و یک Promise را برمیگرداند که زمانیresolve
میشود که تمام Promiseهای ورودیsettled
(یعنی یاfulfilled
یاrejected
) شده باشند. این برای سناریوهایی مفید است که میخواهید نتایج تمام درخواستها را بدانید، حتی اگر برخی از آنها ناموفق بوده باشند.Promise.race(iterable)
: آرایهای از Promiseها را میگیرد و Promise را برمیگرداند که به محض اینکه یکی از Promiseهای موجود در آرایهresolve
یاreject
شود،resolve
یاreject
میشود.
async function fetchMultipleData() {
const userUrl = 'https://api.example.com/users/1';
const postsUrl = 'https://api.example.com/users/1/posts';
const commentsUrl = 'https://api.example.com/comments?postId=1';
try {
const [userData, postsData, commentsData] = await Promise.all([
fetch(userUrl).then(res => {
if (!res.ok) throw new Error(`User data fetch failed: ${res.status}`);
return res.json();
}),
fetch(postsUrl).then(res => {
if (!res.ok) throw new Error(`Posts data fetch failed: ${res.status}`);
return res.json();
}),
fetch(commentsUrl).then(res => {
if (!res.ok) throw new Error(`Comments data fetch failed: ${res.status}`);
return res.json();
})
]);
console.log('User Data:', userData);
console.log('Posts Data:', postsData);
console.log('Comments Data:', commentsData);
} catch (error) {
console.error('One of the concurrent fetches failed:', error);
}
// مثال با Promise.allSettled
try {
const results = await Promise.allSettled([
fetch('https://api.example.com/valid-endpoint').then(res => res.json()),
fetch('https://api.example.com/invalid-endpoint').then(res => res.json()), // این احتمالا خطا میدهد
Promise.resolve('Some resolved value')
]);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index} fulfilled with value:`, result.value);
} else {
console.warn(`Promise ${index} rejected with reason:`, result.reason);
}
});
} catch (error) {
// این بلاک تنها زمانی اجرا میشود که Promise.allSettled خودش خطایی داشته باشد، نه Promiseهای داخلی
console.error("This catch block won't typically be hit for individual promise rejections with allSettled.");
}
}
// fetchMultipleData();
نتیجهگیری
همانطور که دیدیم، تعامل با APIها ستون فقرات برنامههای وب مدرن است. جاوا اسکریپت دو ابزار اصلی برای این کار ارائه میدهد: XMLHttpRequest
(AJAX) و Fetch API
. در حالی که XHR نقش پیشگامانه خود را در فعال کردن ارتباطات ناهمزمان ایفا کرد و هنوز هم در کدهای legacy یا در کتابخانههایی مانند Axios برای سازگاری با مرورگرهای قدیمی استفاده میشود، Fetch API
با اینترفیس مبتنی بر Promise و سینتکس تمیزتر خود، انتخاب ارجح برای توسعه وب مدرن است.
با درک عمیق از پروتکل HTTP، مدیریت موثر خطاها، بهکارگیری الگوهای پیشرفته مانند async/await
، و استفاده از ابزارهای کمکی مانند Axios، توسعهدهندگان میتوانند برنامههای وب کارآمد، قابل اعتماد و کاربرپسندی بسازند که به طور مؤثر با منابع داده ارتباط برقرار کنند.
تسلط بر این مفاهیم و تکنیکها، نه تنها به شما کمک میکند تا کدهای بهتری بنویسید، بلکه توانایی شما را در اشکالزدایی مشکلات شبکه و ساخت برنامههای مقیاسپذیر و مقاوم در برابر خطا نیز افزایش میدهد. همواره بهترین روشها را رعایت کنید و از ابزارهای مدرن برای سادهسازی فرآیندهای توسعه خود بهره ببرید.
“تسلط به برنامهنویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT”
"تسلط به برنامهنویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT"
"با شرکت در این دوره جامع و کاربردی، به راحتی مهارتهای برنامهنویسی پایتون را از سطح مبتدی تا پیشرفته با کمک هوش مصنوعی ChatGPT بیاموزید. این دوره، با بیش از 6 ساعت محتوای آموزشی، شما را قادر میسازد تا به سرعت الگوریتمهای پیچیده را درک کرده و اپلیکیشنهای هوشمند ایجاد کنید. مناسب برای تمامی سطوح با زیرنویس فارسی حرفهای و امکان دانلود و تماشای آنلاین."
ویژگیهای کلیدی:
بدون نیاز به تجربه قبلی برنامهنویسی
زیرنویس فارسی با ترجمه حرفهای
۳۰ ٪ تخفیف ویژه برای دانشجویان و دانش آموزان