פניה אסינכרונית והמסתעף Angular TS
-
יש לי
http//GetBooks
- API שמחזיר מערך של אובייקטיםמסוגBook
המייצגים ספרים.
אני רוצה להציג את רשימת הספרים בקומפוננטהShowBooks
. בקומפוננטה זו יש חיווי חזותי האם קראתי את הספר או לא התלוי במשתנהhasReaded
הנמצא בתוךBook
.
כל ספר אפשר לקרוא ולצורך כך יש קומפוננטה נפרדתBookReader
.
בסיום הקריאה אני מסמן "קראתי" והחיווי אמור להתעדכן אוטומטי כשאחזור ל-showBooks
.המימוש מתבצע כך: בקונסטרקטור של
ShowBooks
נטען סרויסBooksSvc
, אשר בקונסטרקטור שלו (שלBooksSvc
) מתבצעת פנייה אוטומטית לשרת ואת המערך שהיא מקבלת היא מציבה למערך בתוך הסרויס .
כעת בהמשך הקונסטרקטור שלshowBooks
פשוט מקבלים מהסרויס את רשימת הספרים העדכנית.
showBooks
this.books = BooksSvc.GetBooks();
BookSvc
GetBooksURL: string = "http:GetBooks"; books: Book[] = []; constructor(private httpClient: HttpClient) { this.firstGetBooks().subscribe(b => { this.books =b; }) } firstGetBooks(): Observable<Book[]> { return this.httpClient.get<Book[]>(this.GetBooksURL) } Getbooks() { return this.books }
הבעיה היא שכשהקומפוננטה
showBooks
מבקשת את המערך מהסרוויס עדין לא תמיד המידע הגיע. הצלחתי לפתור את זה עם setTimeOut אבל זה פתרון גרוע לדעתי כי אתה לא יודע כמה זמן יקח למידע להגיע וזה או בזבוז זמן או שתיפול על פחות מידי זמן.
איך אפשר לפתור את זה? -
פוסט זה נמחק!
-
השורה הזו
this.books = BooksSvc.GetBooks();
אמורה להחזיר מערך שלא קיים.
המתודה GetBooks לא אמורה להחזיר מערך, אלא פרומיס.
כך אמור להיראות הסרביסGetBooksURL: string = "http:GetBooks"; books: Promise<Book[]>; constructor(private httpClient: HttpClient) { this.GetBooks(); } Getbooks() { return this.books = this.books || this.httpClient.get<Book[]>(this.GetBooksURL).toPromise(); }
והקומפוננטה תחכה לפרומיס כך
this.books = await BooksSvc.GetBooks();
-
לא חייבים להפוך לפרומיס, observable הוא מצוין ויכול לעשות הרבה דברים שpromise לא יודע לעשות.
כמו"כ למה לקרוא לhttp מתוך הconstructor של הservice? מקובל ליצור פונקציה בתוך הservice שהיא מחזירה את הobsevable של הקריאה. הconstructor משמש להזרקה של services אחרים לתוכו.
אפשר לשים את הפונקציה שקוראת לhttp ישירות בתוך הhtml עם שימוש ב async pipe
https://angular.io/guide/observables-in-angular#async-pipe -
@יוסף-בן-שמעון כתב בפניה אסינכרונית והמסתעף Angular TS:
this.books = await BooksSvc.GetBooks();
הכנסתי את השורה הזו ל-CTOR וקיבלתי שגיאה
'await' expressions are only allowed within async functions and at the top levels of modules
הכנסתי את הנ"ל לפונקציה נוספת בתוך showBooksasync getBooks() { await this.booksSvc.getBooks() }
והשארתי את הקריאה מקונסטרקטור כך:
this.books= this.getBooks()
כתוצאה מכך אני מקבל שגיאה
Type 'Promise<void>' is missing the following properties from type 'Book[]': length, pop, push, concat, and 29 more.
בקיצור לא הולך...
-
@WEB כתב בפניה אסינכרונית והמסתעף Angular TS:
לא חייבים להפוך לפרומיס, observable הוא מצוין ויכול לעשות הרבה דברים שpromise לא יודע לעשות.
אני משתמש ב-observable כדי לקבל מהשרת, אבל אני לא יודע להשתמש בו שרק כאשר יתאכלסו הנתונים מהשרת אל תוך הסרוויס, אז ישלף המידע.
כמו"כ למה לקרוא לhttp מתוך הconstructor של הservice? מקובל ליצור פונקציה בתוך הservice שהיא מחזירה את הobsevable של הקריאה. הconstructor משמש להזרקה של services אחרים לתוכו.
כי חשבתי שיהיה הגיוני פעם אחת לשלוף את המידע ולא כל פעם לבדוק האם המידע קיים או לא.
-
כתוצאה מכך אני מקבל שגיאה
Type 'Promise<void>' is missing the following properties from type 'Book[]': length, pop, push, concat, and 29 more.
בקיצור לא הולך...
אתה מנסה להציב promise (פונקציית async מחזירה promise) לתוך משתנה מסוג []book. וודאי שתקבל שגיאה.
אני משתמש ב-observable כדי לקבל מהשרת, אבל אני לא יודע להשתמש בו שרק כאשר יתאכלסו הנתונים מהשרת אל תוך הסרוויס, אז ישלף המידע.
כי חשבתי שיהיה הגיוני פעם אחת לשלוף את המידע ולא כל פעם לבדוק האם המידע קיים או לא.
בדיוק בשביל זה יש את הפונקציהsubscribe שרצה כאשר יש ערך מוחזר.
כמו שכתבתי למעלה , אתה יכול להציב פונקציה שמחזירה observable בתוך הhtml עם שימוש ב async pipe
כך{{getBooks() | async}}
-
observable לא נצרך כאן וזה לא נכון להשתמש בו בסיטואציה הזו
מדובר על נתונים שנטענים פעם אחת בלבד מהשרת, והאובייקט הזה לא אמור להתעדכן מהשרת יותר, זו התנהגות פרומיסית קלאסית
observable לא מגיע ככה מהקופסה, אמנם אפשר לקנפג אותו כדי להתאים אותו לצרכים האלו, אבל זה מאולץ ומיותר. -
@yyy כתב בפניה אסינכרונית והמסתעף Angular TS:
בקיצור לא הולך...
באנגולר לא אמורים לעבוד עם הקונסטרקטור
הקומפוננטה עשויה להיראות כך:export class showBooksComponent implements OnInit { books: Book[]; constructor(private BooksSvc: BooksSvc) { } async ngOnInit() { this.books = await this.BooksSvc.GetBooks(); } }
זה יאפשר לך לבצע מניפולציות על המערך מתוך הקומפוננטה.
באופן כללי עדיף להמנע משימוש ב pipes כמו async ובבינדינג לפונקציות, זה מריץ את הפונקציה בכל בינדינג ודורש יותר משאבים. -
@יוסף-בן-שמעון
עשיתי כדבריך:
זה הסרוויסconstructor(private httpClient: HttpClient) { this.getBooks(); } getBooks() { return this.Books = this.Books || this.httpClient.get<Book[]>(this.GetBooksURL).toPromise(); }
זה הקומפוננטה
constructor( private BookSvc: BookSevice ) { } async ngOnInit() { this.books= await this.BookSvc.getBooks(); }
העניין שכעת לא מתבצעת בכלל פניה לשרת.
האמת (אני מניח שזה קשור לזה) שגם כך אשמח להכיר את הסינטקס של ה-return הנ"ל בשני דברים:
א. סימן ה"||" פירושו OR?
ב. מה המשמעות של קטע הקוד הזה
this.books = this.books
?
תודה רבה. -
@yyy כתב בפניה אסינכרונית והמסתעף Angular TS:
עשיתי כדבריך:
כמעט...
איך נראית השורה הזו בסרביס? כמו האופציה הראשונה או השניה?books: Promise<Book[]>; books: Promise<Book[]> = [];
-
@yyy כתב בפניה אסינכרונית והמסתעף Angular TS:
האמת (אני מניח שזה קשור לזה) שגם כך אשמח להכיר את הסינטקס של ה-return הנ"ל בשני דברים:
א. סימן ה"||" פירושו OR?
ב. מה המשמעות של קטע הקוד הזה
this.books = this.books
?אכן, זה אופרטור OR.
כשאתה מבצע השמה למשתנה books אתה קודם כל בודק אם הוא כבר מאותחל, במידה והוא כבר אותחל אתה לא משנה בו כלום אלא מצביע עליו בחזרה, אם הוא עוד לא מאותחל והוא מחזיר undefined (שזה ערך false ב JS) אז אתה מאתחל אותו כפרומיס.
כך אתה מרויח שהפניה לשרת תתבצע רק פעם אחת בכל חיי האפליקציה, וכל מי שירצה לקבל את רשימת הספרים יקבל אותה מהפרומיס הזה -
@יוסף-בן-שמעון כתב בפניה אסינכרונית והמסתעף Angular TS:
books: Promise<Book[]>;
books: Promise<Book[]> = [];לא כך ולא כך, אלא כך:
books:Books[] = [];
זה:
books: Promise<Books[]>;
מחזיר את השגיאה הזו
'books' has no initializer and is not definitely assigned in the constructor.
ואז אני מוסיף סימן?
ומקבל שגיאה ב-returnType 'Promise<book[] | undefined>' is not assignable to type 'Promise<book[]>'. Type book[] | undefined' is not assignable to type book[]'. Type 'undefined' is not assignable to type book[]'.ts(2322)
וזה:
books: Promise<Book[]> = [];
מחזיר שגיאה:
Type 'never[]' is missing the following properties from type 'Promise<Book[]>': then, catch, finally, [Symbol.toStringTag]
-
@yyy כתב בפניה אסינכרונית והמסתעף Angular TS:
זה:
books: Promise<Books[]>;
מחזיר את השגיאה הזו
'books' has no initializer and is not definitely assigned in the constructor.אתה יכול לכתוב במקום סימן שאלה סימן קריאה, וזה ידלג על הבדיקה
או לאתחל את זה בקונסטקטור בלי האופרטור || ובמתודה getBooks רק להחזיר אותו בלי לעשות השמה -
@יוסף-בן-שמעון כתב בפניה אסינכרונית והמסתעף Angular TS:
כשאתה מבצע השמה למשתנה books אתה קודם כל בודק אם הוא כבר מאותחל,
זה נכון לגבי כל השמה, או שזה משהו ספציפי לפקודת return?
במידה והוא כבר אותחל אתה לא משנה בו כלום אלא מצביע עליו בחזרה,
למה פקודה כזו
return this.books || this.httpClient.get<Books []>(this.Getbooks URL).toPromise();
לא מבצעת אותו דבר, הרי אם האובייקט אינו מאותחל זה יחזיר undefind כלומר false, וילך לפרומיס לאתחל את האובייקט, ואם זה כן מאותחל כלומר true שיחזיר אותו עצמו ולא יפנה לפרומייס?
בדקתי ואכן בסינטקס שלי הוא כן פונה כל פעם מחדש ואני רק שואל למה זה קורה. -
אגב ה-toPromise בהקשר זה הוא depricated
-
@yyy כתב בפניה אסינכרונית והמסתעף Angular TS:
למה פקודה כזו
return this.books || this.httpClient.get<Books []>(this.Getbooks URL).toPromise();
לא מבצעת אותו דבר, הרי אם האובייקט אינו מאותחל זה יחזיר undefind כלומר false, וילך לפרומיס לאתחל את האובייקט, ואם זה כן מאותחל כלומר true שיחזיר אותו עצמו ולא יפנה לפרומייס?
בדקתי ואכן בסינטקס שלי הוא כן פונה כל פעם מחדש ואני רק שואל למה זה קורה.כי אתה אף פעם לא מאתחל אותו.
-
@yyy כתב בפניה אסינכרונית והמסתעף Angular TS:
זה נכון לגבי כל השמה, או שזה משהו ספציפי לפקודת return?
כל השמה
@yyy כתב בפניה אסינכרונית והמסתעף Angular TS:
למה פקודה כזו
returnthis.books || this.httpClient.get<Books []>(this.Getbooks URL).toPromise();
לא מבצעת אותו דבר, הרי אם האובייקט אינו מאותחל זה יחזיר undefind כלומר false, וילך לפרומיס לאתחל את האובייקט, ואם זה כן מאותחל כלומר true שיחזיר אותו עצמו ולא יפנה לפרומייס?
בדקתי ואכן בסינטקס שלי הוא כן פונה כל פעם מחדש ואני רק שואל למה זה קורה.כפי שכתב אהרן, בתחביר שלך אתה לא מאתחל אותו אף פעם, אז הוא תמיד נשאר undefined, ולכן כך קריאה לפונקציה תייצר פניה לשרת.
אם תנסה לפרק את הסינטקס הזה לגורמים, זה יראה כך:getBooks() { if (this.books !== undefined) { return this.books; } else { this.books = this.httpClient.get<Book[]>(this.GetBooksURL).toPromise(); // <<<<<<<<<<<<<<< return this.books; } }
אצלך דילגת על שורה 5, ובמקום זה עשית כך:
getBooks() { if (this.books !== undefined) { return this.books; } else { return this.httpClient.get<Book[]>(this.GetBooksURL).toPromise(); } }
אז books מעולם לא אותחל
-
@יוסף-בן-שמעון
הטעות שלי היית שחשבתי שהתכוונת
לזהreturn (this.Books = this.Books )|| this.httpClient.get<Book[]>(this.GetBooksURL).toPromise();
ולא הבנתי מה ההצבה המוזרה באופרנד הראשון
בשעה שהתכוונת באמת לזה:return this.Books = (this.Books || this.httpClient.get<Book[]>(this.GetBooksURL).toPromise());
-
עכשיו ברשותכם שאלות:
א.
הפונקציה בסרוויסgetBooks() { return this.Books = this.Books || this.httpClient.get<Book[]>(this.GetBooksURL).toPromise(); }
מחזירה פרומיס
אבל הפונקציה בקומפוננטהasync ngOnInit() { this.books= await this.BookSvc.getBooks();
מקבלת []Book ,
השאלה היא האם הסיבה שמתבצעת ההמרה האוטומטית הזו, למרות שמדובר ב-TS, זה בגלל שיש async/await הוא מבין שהוא צריך לחלץ את הערך מהפרומיס?ב. אני מנסה לחבר את פרומיס זה לרעיון הכללי של פרומיס. אני ידעתי שהמצב של הפרומיס מוגדר ע"י הרצת resolve/reject, ואז פונים לthen או catch וכדו'. השאלה היא מי מריץ כאן מאחור את כל הפונקציות האלו הרי בקוד אני לא נוגע כאן ב-resolve then ודומיהם?