באגים שלעולם לא יצוצו, עד ש…

March 31, 2008

tags: , ,
3 comments


לפני כמה שבועות ייעצתי לאיזשהו ארגון בנושא AJAX שמבלי להרחיב יותר מדי, מממש מעין UpdatePanel בעצמו מבלי להשתמש ב-UpdatePanel ה-AJAX-י.


כשהגענו לשלב של טעינה דינאמית של בלוק Script, התחלנו להתקע בבעיות שנובעות מהרמה לא סדירה של אירועי readyStateChange ב-IE.


בסופו של דבר החלטנו שאין חכם כבעל הנסיון, והשתמשנו ישירות בפונקציות ה-JavaScript של UpdatePanel עצמו (Sys.WebForms) ושם נתקלנו במשהו מוזר …


בקוד המיקרוסופטי נמצאת הפונקציה הבאה:



   1: Sys._ScriptLoader.isScriptLoaded = function Sys$_ScriptLoader$isScriptLoaded(scriptSrc) {
   2:     // For Firefox we need to resolve the script src attribute
   3:     // since the script elements already in the DOM are always
   4:     // resolved. To do this we create a dummy element to see
   5:     // what it would resolve to.
   6:     var dummyScript = document.createElement(‘script’);
   7:     dummyScript.src = scriptSrc;
   8:     return Array.contains(Sys._ScriptLoader._getLoadedScripts(), dummyScript.src);
   9: }


בשורה האחרונה בפונקציה מופיעה פניה ל-getLoadedScripts שנראית כך :



   1: Sys._ScriptLoader._getLoadedScripts = function Sys$_ScriptLoader$_getLoadedScripts() {
   2:     if(!Sys._ScriptLoader._referencedScripts) {
   3:         Sys._ScriptLoader._referencedScripts = [];
   4:         Sys._ScriptLoader.readLoadedScripts();
   5:     }
   6:     return Sys._ScriptLoader._referencedScripts;
   7: }


 


בואו נבחן את הקוד לרגע :


1. מבוצעת בדיקה אם האובייקט אותחל


2. אם הוא לא אותחל עד כה, הוא מאותחל ומופעלת הפונקציה readLoadedScript.


הפונקציה הנ”ל נראית כך :



   1: Sys._ScriptLoader.readLoadedScripts = function Sys$_ScriptLoader$readLoadedScripts() {
   2:     // enumerates the SCRIPT elements in the DOM and ensures we have their SRC’s in the referencedScripts array.
   3:     if(!Sys._ScriptLoader._referencedScripts) {
   4:         var referencedScripts = Sys._ScriptLoader._referencedScripts = [];
   5:  
   6:         var existingScripts = document.getElementsByTagName(‘SCRIPT’);
   7:         for (i = existingScripts.length – 1; i >= 0; i–) {
   8:             var scriptNode = existingScripts[i];
   9:             var scriptSrc = scriptNode.src;
  10:             if (scriptSrc.length) {
  11:                 if (!Array.contains(referencedScripts, scriptSrc)) {
  12:                     Array.add(referencedScripts, scriptSrc);
  13:                 }
  14:             }
  15:         }
  16:     }
  17: }


תקראו את הקוד ותנסו להבין מה לא מסתדר ? לא מצאתם ? תסתכלו שוב, קחו חצי דקה לקרוא … למי שלא ראה שיגלול למטה


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


.


אם רגע לפני הקריאה לפונקציה אתחלנו את האובייקט (שורה 3 בקטע הקוד השני), למה שהוא שוב יהיה ריק (שורה 3 בקוד האחרון) ? הקוד מעולם לא נכנס ל-if !! הפונקציה כלל לא אמורה לעבוד !! מה שאומר שהפונקציה isScriptLoaded תמיד מחזירה false !!


על-פניו הטענה נשמעת הגיונית, אבל צריך לזכור ש-UpdatePanel דווקא מתפקד מצוין ואף פעם לא מקבלים אצלו הודעת שגיאה כאשר מנסים להטעין את אותו קובץ JS פעמיים … אז מה קרה פה ?


אם חוקרים קצת יותר לעומק את הקוד של Sys.WebForms, אפשר לראות שהפונקציה readLoadedScript מופעלת באופן יזום מייד אחרי שה-WebRequest מסיים לעבוד והרבה לפני שקוד ה-AJAX מנסה לטעון את הסקריפטים החדשים שהגיעו מהשרת.


 


כנראה מי שאחראי על הקוד הזה בצוות ברדמונד אמר לעצמו “אויש ויי איזמיר, יש פה באג לוגי שיגרום לכך שהקוד הזה לא ירוץ אף פעם, אבל בעצם אנחנו מפעילים את הפונקציה readLoadedScripts ישירות לפני ש-isScriptLoaded מופעל בפעם הראשונה, אז זה מכסה לנו את כל המקרים … חוץ אולי מהמקרה שתוכניתן כלשהו יתעקש להשתמש בקוד שלנו מבלי להשתמש עם UpdatePanel … המממ, שאני אתקן עכשיו את הקוד ? … אוף, מחר צריך להוציא גרסה. אין לי זמן לזה. מי שמתעקש להשתמש עם קוד JS-י לא שלו, שיאכל מרורים ושיעבור ל-UpdatePanel …”


 


אז מה למדנו מזה ?


1. גם בקוד RTM יש באגים (כמה מפתיע !)


2. תוכניתנים אוהבים להתעלל בקוד


3. כשמשתמשים ישירות בקוד פנימי של מישהו, יש לקחת בחשבון שהוא התעלם ממקרי קצה שהוא לא יזם


4. תוכניתנים יהודים אוכלים מרורים לא רק בפסח


 

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

3 comments

  1. Ken EgoziMarch 31, 2008 ב 6:14 pm

    הי עידו.
    1. נסה להוסיף לקטעי הקוד שלך overflow-x:scroll כי חלק מהקוד נעלם בצד.

    2. בגלל “מוזרויות” כאלו אני מעדיף להשתמש ב jQuery, ו/או PrototypeJS. הרבה יותר פשוט.

    Reply
  2. Moshe LMarch 31, 2008 ב 6:14 pm

    מעניין.
    אני לרוב מעדיף לא להשתמש בספריות האלה אלא פשוט ליצור את ה-JS בעצמי. פשוט הרבה יותר.

    Reply
  3. אוהד אסטוןMarch 31, 2008 ב 6:51 pm

    דווקא jQuery זה fw ממש ממש ממש חזק, ואני לא רואה סיבה לשכתב אותו.

    עם prototype לא עבדתי.

    אוהד.

    Reply