fetch زودتر از موقع resolve میشه، بدنهش هنوز تو راهه
خلاصهٔ کاملتر
خیلیا فکر میکنن وقتی await fetch(url) تموم شد، کل پاسخ رسیده؛ ولی این await فقط منتظر header هاست. یعنی وقتی promise حل میشه، بدنهی پاسخ هنوز داره از رو کانکشن میاد و کد بعد از await شروع میشه در حالی که داده کامل نرسیده. تو یه تست ساده، header ها بعد از ۲۵۰ میلیثانیه آماده شدن ولی بدنه تا ۳۴۴۵ میلیثانیه بعد کامل نشد:
const response = await fetch(url) // header ها آماده شدن
const body = await response.json() // بدنه کامل شداین فاصله معمولاً خیلی کوچیکه و به چشم نمیاد، ولی وقتی بدنه رو اصلاً نخونی مهم میشه. تو Node، اگه بعد از چک کردن status بدنه رو نخونی و ردش کنی، زیر ۱۶ کیلوبایت فرقی نداره ولی بالاتر از اون هر پاسخ نخونده یه کانکشن جدا مصرف میکنه - مثلاً ۱۰ تا درخواست پشتسرهم بهجای ۲ کانکشن، ۱۰ کانکشن باز میکنه. تو کروم این مرز خیلی بالاتره، یهجایی بین ۵۱۲ کیلوبایت تا ۴ مگابایت. حتی response.body.cancel() هم کانکشن رو پس نمیگیره - فقط خوندن کامل بدنه این کارو میکنه.
نویسنده دربارهی clone() هم توضیح میده: خیلیا فکر میکنن clone یه کپی واقعی از بدنه میسازه، ولی طبق اسپک فقط استریم (یعنی همون جریان بایتهایی که از سوکت میاد) رو دو شاخه میکنه. اگه یه شاخه هیچوقت خونده نشه، بایتهاش نگه داشته میشن تا شاخهی دیگه بخوادشون - و همین تجمع حافظه میسازه. تو تست نویسنده روی یه پاسخ ۶۰ مگابایتی، حالت بدون clone حدود ۱۱۶ مگابایت مصرف کرد ولی حالت با clone نزدیک ۱۷۰ مگابایت.
به گفتهی نویسنده این مشکل یهو خودشو نشون نمیده: سرور همچنان جواب میده و درخواستهای دیگه هم سریع جواب میگیرن، فقط تعداد کانکشنهای باز کمکم بالا میره - چیزی که میتونه ماهها تو پروداکشن بمونه بدون اینکه کسی بفهمه چرا.
نکات کلیدی:
- await fetch فقط منتظر header هاست، نه بدنهی کامل پاسخ
- تو Node، نخوندن بدنهی پاسخهای بالای ۱۶ کیلوبایت یه کانکشن جدا مصرف میکنه؛ تو کروم این مرز بین ۵۱۲ کیلوبایت و ۴ مگابایته
- clone() بدنه رو کپی نمیکنه، فقط استریم رو دو شاخه میکنه؛ نخوندن یه شاخه باعث تجمع حافظه میشه
- response.body.cancel() هم کانکشن رو پس نمیگیره - فقط خوندن کامل بدنه این کارو میکنه




