כדי למנוע מהאפליקציה להיעלם בתוך הודעות WebSocket או להציף שרת WebSocket בהודעות, אפשר להפעיל לחץ חזרה.
רקע
WebSocket API מספק ממשק JavaScript לפרוטוקול WebSocket, שמאפשר לפתוח סשן תקשורת אינטראקטיבי דו-כיווני בין הדפדפן של המשתמש לבין שרת. באמצעות ה-API הזה אפשר לשלוח הודעות לשרת ולקבל תשובות מבוססות-אירועים בלי לבצע סקרים לשרת כדי לקבל תשובה.
Streams API
Streams API מאפשר ל-JavaScript לגשת באופן פרוגרמטי למקורות של מקטעי נתונים שמתקבלים ברשת ולעבד אותם לפי הצורך. מושג חשוב בהקשר של שידורים הוא לחץ חזרה. זהו התהליך שבו מקור נתונים יחיד או שרשרת צינורות קובעים את מהירות הקריאה או הכתיבה. כשהסטרימינג עצמו או סטרימינג בשלב מאוחר יותר בשרשרת הצינור עדיין עסוק ולא מוכן לקבל עוד קטעים, הוא שולח אות לאחור דרך השרשרת כדי להאט את ההעברה בהתאם.
הבעיה ב-WebSocket API הנוכחי
אי אפשר להפעיל לחץ חזרה על הודעות שהתקבלו
בממשק ה-WebSocket API הנוכחי, התגובה להודעה מתרחשת ב-WebSocket.onmessage
, EventHandler
שנקרא כשמתקבלת הודעה מהשרת.
נניח שיש לכם אפליקציה שצריכה לבצע פעולות עיבוד נתונים כבדות בכל פעם שמתקבלת הודעה חדשה.
סביר להניח שתגדירו את התהליך באופן דומה לקוד שבהמשך, וכיוון שאתם await
את התוצאה של הקריאה ל-process()
, הכל אמור להיות בסדר, נכון?
// A heavy data crunching operation.
const process = async (data) => {
return new Promise((resolve) => {
window.setTimeout(() => {
console.log('WebSocket message processed:', data);
return resolve('done');
}, 1000);
});
};
webSocket.onmessage = async (event) => {
const data = event.data;
// Await the result of the processing step in the message handler.
await process(data);
};
לא נכון! הבעיה ב-WebSocket API הנוכחי היא שאין דרך להפעיל לחץ חוזר.
כשהודעות מגיעות מהר יותר מהשיטה process()
יכולה לטפל בהן, תהליך הרינדור יאגור את ההודעות האלה בזיכרון, לא יגיב בגלל שימוש של 100% במעבד או יעשה את שניהם.
הפעלת לחץ חזרה על הודעות שנשלחו היא לא ארגונומית
אפשר להפעיל לחץ חוזר על הודעות שנשלחו, אבל זה כרוך בסקרים של המאפיין WebSocket.bufferedAmount
, שהם לא יעילים ולא ארגונומיים.
הנכס הזה לקריאה בלבד מחזיר את מספר הבייטים של הנתונים שנצברו בתור באמצעות קריאות ל-WebSocket.send()
, אבל עדיין לא הועברו לרשת.
הערך הזה מתאפס אחרי שכל הנתונים שנמצאים בתור נשלחים, אבל אם ממשיכים לבצע קריאה ל-WebSocket.send()
, הערך ימשיך לעלות.
מהו WebSocketStream API?
כדי לטפל בבעיה של לחץ חזרה לא קיים או לא ארגונומי, נעשה שימוש ב-WebSocketStream API שמשלב בין סטרימינג ל-WebSocket API. כלומר, אפשר להפעיל לחץ חזרה "בחינם", ללא עלות נוספת.
תרחישים לדוגמה לשימוש ב-WebSocketStream API
דוגמאות לאתרים שיכולים להשתמש ב-API הזה:
- אפליקציות WebSocket עם רוחב פס גבוה שצריכות לשמור על אינטראקטיביות, במיוחד שיתוף מסך וסרטונים.
- באופן דומה, צילום וידאו ואפליקציות אחרות שיוצרות הרבה נתונים בדפדפן שצריך להעלות לשרת. בעזרת לחץ חוזר, הלקוח יכול להפסיק לייצר נתונים במקום לצבור נתונים בזיכרון.
הסטטוס הנוכחי
שלב | סטטוס |
---|---|
1. יצירת הסבר | הושלם |
2. יצירת טיוטה ראשונית של המפרט | בטיפול |
3. איסוף משוב וביצוע שינויים בעיצוב | בטיפול |
4. גרסת מקור לניסיון | הושלם |
5. הפעלה | עוד לא התחילה |
איך משתמשים ב-WebSocketStream API
WebSocketStream API מבוסס על הבטחה (promise), כך שקל לעבוד איתו בעולם JavaScript מודרני.
מתחילים ביצירה של WebSocketStream
חדש ומעבירים לו את כתובת ה-URL של שרת ה-WebSocket.
לאחר מכן, ממתינים שהחיבור יהיה opened
, כתוצאה מכך מופיעה ההודעה ReadableStream
ו/או ההודעה WritableStream
.
אחרי שמפעילים את השיטה ReadableStream.getReader()
, מקבלים ReadableStreamDefaultReader
, שממנו אפשר read()
לקבל נתונים עד שהזרם מסתיים, כלומר עד שהוא מחזיר אובייקט מהפורמט {value: undefined, done: true}
.
לכן, כשמפעילים את השיטה WritableStream.getWriter()
, מקבלים בסופו של דבר WritableStreamDefaultWriter
, שאפשר להעביר אליו נתונים באמצעות write()
.
const wss = new WebSocketStream(WSS_URL);
const {readable, writable} = await wss.opened;
const reader = readable.getReader();
const writer = writable.getWriter();
while (true) {
const {value, done} = await reader.read();
if (done) {
break;
}
const result = await process(value);
await writer.write(result);
}
לחץ חזרה
מה לגבי התכונה 'לחץ חזרה' שהובטחה?
אתם מקבלים את התכונה "בחינם", בלי לבצע שלבים נוספים.
אם process()
נמשך זמן רב יותר, ההודעה הבאה תטופל רק אחרי צינור עיבוד הנתונים יהיה מוכן.
באופן דומה, השלב WritableStreamDefaultWriter.write()
מתבצע רק אם ניתן לעשות זאת בצורה בטוחה.
דוגמאות מתקדמות
הארגומנט השני של WebSocketStream הוא תיקיית אפשרויות שמאפשרת הרחבה עתידית.
האפשרות היחידה היא protocols
, שמתנהגת כמו הארגומנט השני ל-constructor של WebSocket:
const chatWSS = new WebSocketStream(CHAT_URL, {protocols: ['chat', 'chatv2']});
const {protocol} = await chatWSS.opened;
הערך שנבחר של protocol
וגם הערך הפוטנציאלי של extensions
הם חלק מהמילון שזמין דרך ההבטחה WebSocketStream.opened
.
כל המידע על החיבור החי מופיע בהתחייבות הזו, כי הוא לא רלוונטי אם החיבור נכשל.
const {readable, writable, protocol, extensions} = await chatWSS.opened;
מידע על חיבור WebSocketStream סגור
המידע שהיה זמין מהאירועים WebSocket.onclose
ו-WebSocket.onerror
ב-WebSocket API זמין עכשיו דרך ההבטחה WebSocketStream.closed
.
ההתחייבות נדחית במקרה של סגירה לא תקינה, אחרת היא מתייחסת לקוד ולסיבה שנשלחו מהשרת.
כל קודי הסטטוס האפשריים והמשמעות שלהם מפורטים ברשימת קודי הסטטוס CloseEvent
.
const {code, reason} = await chatWSS.closed;
סגירת חיבור של WebSocketStream
אפשר לסגור את WebSocketStream באמצעות AbortController
.
לכן, מעבירים AbortSignal
ל-constructor של WebSocketStream
.
const controller = new AbortController();
const wss = new WebSocketStream(URL, {signal: controller.signal});
setTimeout(() => controller.abort(), 1000);
לחלופין, אפשר להשתמש גם בשיטה WebSocketStream.close()
, אבל המטרה העיקרית שלה היא לאפשר לציין את הקוד ואת הסיבה שנשלחים לשרת.
wss.close({code: 4000, reason: 'Game over'});
שיפור הדרגתי ויכולת פעולה הדדית
Chrome הוא הדפדפן היחיד שבו מוטמע כרגע WebSocketStream API.
כדי לאפשר יכולת פעולה הדדית עם WebSocket API הקלאסי, אי אפשר להפעיל לחץ חזרה על הודעות שהתקבלו.
אפשר להפעיל לחץ חוזר על הודעות שנשלחו, אבל זה כרוך בסקרים של המאפיין WebSocket.bufferedAmount
, שהם לא יעילים ולא ארגונומיים.
זיהוי תכונות
כדי לבדוק אם יש תמיכה ב-WebSocketStream API, משתמשים בקוד הבא:
if ('WebSocketStream' in window) {
// `WebSocketStream` is supported!
}
הדגמה (דמו)
בדפדפנים נתמכים, אפשר לראות את WebSocketStream API בפעולה ב-iframe המוטמע, או ישירות ב-Glitch.
משוב
צוות Chrome רוצה לשמוע על החוויה שלכם עם WebSocketStream API.
תיאור של עיצוב ה-API
האם יש משהו ב-API שלא פועל כצפוי? או אולי חסרות שיטות או מאפיינים שדרושים לכם כדי להטמיע את הרעיון? יש לכם שאלות או הערות לגבי מודל האבטחה? אתם יכולים לשלוח דיווח על בעיה במפרט במאגר GitHub המתאים, או להוסיף את המחשבות שלכם לבעיה קיימת.
דיווח על בעיה בהטמעה
מצאתם באג בהטמעה של Chrome?
או שההטמעה שונה מהמפרט?
שולחים דיווח על באג בכתובת new.crbug.com. חשוב לכלול כמה שיותר פרטים, הוראות פשוטות לשחזור הבעיה ולהזין את הערך Blink>Network>WebSockets
בתיבה Components.
Glitch הוא כלי מצוין לשיתוף מקרים של שחזור מהיר וקל.
תמיכה ב-API
אתם מתכננים להשתמש ב-WebSocketStream API? התמיכה הציבורית שלכם עוזרת לצוות Chrome לתת עדיפות לתכונות, ומראה לספקי דפדפנים אחרים כמה חשובה התמיכה בהן.
שולחים ציוץ אל @ChromiumDev עם ההאשטאג #WebSocketStream
ומציינים איפה ואיך אתם משתמשים בו.
קישורים שימושיים
- הסבר ציבורי
- דוגמה ל-WebSocketStream API | מקור הדוגמה ל-WebSocketStream API
- באג במעקב
- הערך ב-ChromeStatus.com
- רכיב Blink:
Blink>Network>WebSockets
תודות
אדם רייס ו-יוטקה הירונו הטמיעו את WebSocketStream API.