Fetch הוא לקוח HTTP מבוסס promises
fetch מובנה בדפדפנים וב-Node מודרני. נותנים לו URL, והוא מחזיר Promise שמתממש לאובייקט Response. זה כל ה-API בבסיסו:
יש שתי קריאות .then כי יש שני שלבים אסינכרוניים: קודם מגיעות הכותרות של התגובה (עם זה ה-promise הראשון מתממש), ואחר כך הגוף נקרא ומפוענח (response.json() הוא בעצמו promise). הגוף לא מורד עד שמבקשים אותו.
אותו תהליך עם async/await נקרא כמו קוד רגיל מלמעלה למטה:
שני await, שתי נקודות השהיה. אותה עבודה, סדר קריאה ברור יותר.
אובייקט ה-Response
מה שמקבלים בחזרה הוא לא הגוף, אלא אובייקט Response עם מטא-דאטה ומתודות לקריאת הגוף בצורות שונות:
אפשר לקרוא את הגוף עם .json(), .text(), .blob(), .arrayBuffer() או .formData(). כל אחת מהן מחזירה promise. אפשר לקרוא את הגוף רק פעם אחת: קראו ל-.json() פעמיים על אותה תגובה, והקריאה השנייה תזרוק שגיאה.
המלכודת הגדולה: שגיאות HTTP לא גורמות לדחייה
זה מכשיל כמעט כל מי שחדש ב-fetch. תגובת 404 או 500 היא לא דחייה. ה-promise מתממש כרגיל, עם response.ok === false. fetch נדחה רק כשהבקשה עצמה לא הצליחה להסתיים: כשל DNS, אין רשת, חסימת CORS.
המשמעות היא ש-fetch נאיבי ימסור לכם בשמחה דף שגיאה ויקרוס מאוחר יותר ב-.json():
התיקון הוא לבדוק בעצמכם את response.ok ולזרוק שגיאה אם השרת החזיר סטטוס שגיאה:
התרגלו לכתוב את בלוק ה-if (!response.ok) הזה. הוא שייך לכל wrapper של fetch שתכתבו.
שליחת בקשת POST
GET הוא ברירת המחדל. לכל דבר אחר, העבירו ארגומנט שני, אובייקט אפשרויות:
שלושה דברים ששווה לשים לב אליהם:
- ברירת המחדל של
methodהיא"GET". הגדירו אותו במפורש ל-POST, PUT, DELETE, PATCH. bodyמקבל מחרוזת (אוFormData,Blobוכו'): fetch לא יסרלז אובייקטים בשבילכם. ה-JSON.stringify(...)באחריותכם.- הכותרת
Content-Typeאומרת לשרת איך לפענח את הגוף. תשכחו אותה, ורוב השרתים יתייחסו לגוף כטקסט רגיל.
Headers, query strings ואפשרויות נוספות
Headers הם פשוט אובייקט (או מופע של Headers). את ה-query string בונים בעצמכם, בדרך כלל עם URLSearchParams:
URLSearchParams מטפל בקידוד בשבילכם: רווחים, סימני &, יוניקוד. ככה לא תקבלו כתובות שבורות כשבקלט יש תווים שצריך לקודד.
אפשרויות נוספות שתראו בקוד אמיתי: credentials: "include" כדי לשלוח cookies בין מקורות, cache: "no-store" כדי לעקוף את מטמון ה-HTTP, ו-mode: "cors" (בדרך כלל ברירת המחדל) כדי לשלוט בהתנהגות CORS.
ביטול בקשה עם AbortController
לפעמים רוצים לוותר: המשתמש הקליד חיפוש חדש, או שהבקשה לוקחת יותר מדי זמן. AbortController הוא המנגנון לזה:
controller.abort() גורם ל-promise של fetch להידחות עם DOMException שה-name שלו הוא "AbortError". בלוק ה-finally מנקה את ה-timeout, כדי שבקשה מוצלחת לא תשאיר טיימר תלוי.
את התבנית הזו, fetch ועוד timeout ועוד ניקוי, שווה לעטוף בפונקציית עזר ולהשתמש בה בכל מקום.
Wrapper לשימוש חוזר
מחברים את הכול ומקבלים פונקציית עזר קטנה שמטפלת בקוד החוזר פעם אחת:
מקום אחד לשנות headers, מקום אחד לטפל בשגיאות, מקום אחד להתמודד עם תגובות ריקות. כל אפליקציה שאינה טריוויאלית מגיעה בסוף למשהו כזה.
הבא בתור: טיפול בשגיאות בקוד אסינכרוני
fetch הוא אחד המקומות הנפוצים ביותר שבהם צצות שגיאות אסינכרוניות, ובדיקת response.ok היא רק חלק אחד מהפאזל. העמוד הבא עוסק בטיפול בשגיאות ב-promises וב-async/await: לאן שגיאות הולכות, איך תופסים אותן, והמלכודות שנותנות להן לחמוק בשקט.
שאלות נפוצות
איך משתמשים ב-fetch ב-JavaScript?
קראו ל-fetch(url) עם הכתובת שאתם רוצים. הוא מחזיר Promise שמתממש לאובייקט Response. קראו ל-response.json() (שגם הוא promise) כדי לפענח את גוף התגובה. עם async/await: const res = await fetch(url); const data = await res.json();.
איך שולחים בקשת POST עם fetch?
העבירו ארגומנט שני עם method: 'POST', אובייקט headers (בדרך כלל 'Content-Type': 'application/json') ו-body. אובייקטים צריך להפוך למחרוזת עם JSON.stringify(...), כי fetch לא יסרלז את הגוף בשבילכם.
למה fetch לא נדחה ב-404 או ב-500?
fetch נדחה רק בכשלי רשת: שגיאות DNS, אין חיבור, חסימות CORS. מבחינת ה-promise, סטטוסים של שגיאות HTTP הם עדיין תגובות מוצלחות. צריך לבדוק בעצמכם את response.ok (true בין 200 ל-299) או את response.status, ולזרוק שגיאה אם השרת החזיר שגיאה.
אפשר לבטל בקשת fetch?
כן, עם AbortController. צרו אחד, העבירו את ה-signal שלו ל-fetch דרך אובייקט האפשרויות, וקראו ל-controller.abort() כשאתם רוצים לבטל. ה-promise של fetch נדחה עם AbortError שאפשר לטפל בו ב-catch.