aboutsummaryrefslogtreecommitdiff
path: root/files/he/learn/javascript/asynchronous/concepts/index.html
blob: 273c184474cb2c0a657df029514ac310139aba68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
---
title: עקרונות תכנות א-סינכרוני כלליים
slug: Learn/JavaScript/Asynchronous/Concepts
translation_of: Learn/JavaScript/Asynchronous/Concepts
---
<div>{{LearnSidebar}}{{NextMenu("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous")}}</div>

<p>
  במאמר זה אנחנו נעבור על מספר עקרונות חשובים בנושא תכנות א-סינכרוני וכיצד זה נראה בדפדפנים וב-JavaScript. חשוב להבין עקרונות אלו לפני שממשיכים למאמרים הבאים במודול זה.
</p>

<table class="learn-box standard-table">
 <tbody>
  <tr>
   <th scope="row">ידע מוקדם:</th>
   <td>Basic computer literacy, a reasonable understanding of JavaScript fundamentals.</td>
  </tr>
  <tr>
   <th scope="row">מטרה:</th>
   <td>
    הבנה של העקרונות הבסיסיים מאחורי תכנות א-סינכרוני וכיצד אלו באים לידי ביטוי בדפדפנים וב-JavaScript.</td>
  </tr>
 </tbody>
</table>

<h2 id="Asynchronous_-_א-סינכרוני">Asynchronous - א-סינכרוני?</h2>

<p>
  באופן נורמלי, קוד של תוכנית רץ בצורה עצמאית, כאשר רק דבר אחד קורה בכל רגע נתון. אם פונקציה מסויימת נשנעת על התוצאה של פונקציה אחרת, היא חייבית לחכות לפונקציה האחרת לסיים ולהחזיר את התוצאה ועד שזה לא קורה, כל התוכנית עוצרת מנקודת מבטו של המשתמש.</p>

<p>Mac אלו המשתמשים ב-, לדוגמא,
  לפעמים רואים עיגול צבעוני... עיגול זה בעצם אומר למשתמש ״התוכנית הנוכחית שאתה מתמש בה הייתה צריכה לעצור ולחכות שמשהו הסתיים, וזה לוקח זמן..״ </p>

<p><img alt="multi-colored macos beachball busy spinner" src="https://mdn.mozillademos.org/files/16577/beachball.jpg" style="display: block; margin: 0 auto;"></p>

<p>
  זו חווית שימוש מעצבנת, ולא ממש שימוש יעיל ביכולות המחשב, במיוחד בעידן שלמחשבים יש מספר ליבות מעבד זמינות. אין שום סיבה שאנחנו נחכה למשהו כשאנחנו יכולים להמשיך במקביל בעבודה שלנו בזמן שהמחשב עובד על אותה פעולה ויעדכן אותנו כשהפעולה הסתיימה. זה מאפשר לנו לבצע פעולות אחרות במקביל, וזה בעיקרון הבסיס של תכנות א-סינכרוני - <strong>asynchronous programming</strong>. זה תלוי בסביבת הפיתוח שבה אנחנו משתמשים (דפדני אינטרנט במקרה שלנו) להעניק למפתח APIs שיאפשרו לנו להריץ משימות באופן א-סינכרוני


  </p>

<h2 id="Blocking_code">Blocking code</h2>

<p>
  טכניקות א-סינכרוניות הן שימושיות מאוד, במיוחד בפיתוח web. כאשר יישום web רץ בדפדפן ומריץ שורות קוד מבלי להחזיר את השליטה לדפדפן, הדפדפן יכול להיראות כאילו הוא ״קפא״. זה נקרא <strong>blocking</strong>. הדפדפן חסום מלהמשיך ולטפל בפעולות מצד המשתמש ולבצע פעולות נוספות עד אשר יישום ה-web מחזיר את השליטה למעבד.</p>

<p>
  נסתכל על מספר דוגמאות על מנת להסביר למה אנחנו מתכוונים ב- blocking

</p>

<p>ב <a href="https://github.com/mdn/learning-area/tree/master/javascript/asynchronous/introducing">simple-sync.html</a> דוגמא שלנו (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync.html">או כ דף אינטרנט</a>),

  הוספנו מאזין אירוע מסוג click לכפתור כך שבכל פעם שהכפתור יילחץ, הוא יריץ פעולה שלוקחת המון זמן (מחשבת 10 מיליון תאריכים ואז מחזירה את האחרון לקונסולה) ולאחר מכן מוסיפה פסקה ל-DOM: </p>

<pre class="brush: js">const btn = document.querySelector('button');
btn.addEventListener('click', () =&gt; {
  let myDate;
  for(let i = 0; i &lt; 10000000; i++) {
    let date = new Date();
    myDate = date
  }

  console.log(myDate);

  let pElem = document.createElement('p');
  pElem.textContent = 'This is a newly-added paragraph.';
  document.body.appendChild(pElem);
});</pre>

<p>
   כשאתם מריצים את הדוגמא למלעלה, פתחו את הקונסולה ואז לחצו על הכפתור - אתם תשימו לב שהפסקה אינה מופיעה עד אשר חישוב התאיריכים הסתיים והוצגה ההודעה לקונסולה. הקוד רץ בסדר שבה הוא מופיע בקוד המקור והפעולה האחרונה לא תרוץ עד אשר הפעולה הקודמת לא סיימה.</p>

<div class="blockIndicator note">
<p><strong>לתשומת לב</strong>: הדוגמא הקודמת דיי לא מציאותית. אנחנו לעול לא נחשב 10 מיליון תאריכים בתוכנית אמיתית. דוגמא זו נועדה לתת לכם רק את ההבנה של תכנות א-סינכרוני. </p>
</div>

<p>בדוגמא השנייה שלנו, <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/simple-sync-ui-blocking.html">simple-sync-ui-blocking.html</a> (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync-ui-blocking.html">ראו כדף אינטרנט</a>),
  אנחנו מנסים לדמות משהו קצת יותר ריאלי שאתם אולי תיתקלו בו בדף אמיתי. אנחנו חוסמים את האינטראקציה של המשתמש עם העיבוד של ממשק המשתמש. בדוגמא זו יש לנו שני כפתורים:</p>

<ul>
 <li>כפתור בשם "Fill canvas" אשר כאשר הוא נלחץ, אלמנט ה- {{htmlelement("canvas")}} מתמלא במיליון כדורים כחולים. </li>
 <li>כפתור בשם "Click me for alert" אשר כאשר הוא נלחץ מופיעה הודעה למשתמש</li>
</ul>

<pre class="brush: js">function expensiveOperation() {
  for(let i = 0; i &lt; 1000000; i++) {
    ctx.fillStyle = 'rgba(0,0,255, 0.2)';
    ctx.beginPath();
    ctx.arc(random(0, canvas.width), random(0, canvas.height), 10, degToRad(0), degToRad(360), false);
    ctx.fill()
  }
}

fillBtn.addEventListener('click', expensiveOperation);

alertBtn.addEventListener('click', () =&gt;
  alert('You clicked me!')
);</pre>

<p>
  אם תלחצו עלהכפתור הראשון ואז ישר לאחר מכן על השני אתם תראו שההודעה למשתמש לא מופיע עד אשר העיגולים לא סיימו להיות מעובדים. הפעולה הראשונה חוסמת את ביצוע הפעולה השנייה עד אשר הפעולה הראשונה תסתיים. </p>

<div class="blockIndicator note">
<p><strong>לתשומת לב</strong>:
  זהו אמנם נראה לא טוב, אך זוהי רק דוגמא למה שמפתחים נתקלים בה ביישומים אמיתיים. </p>
</div>

<p>
  מדוע זה קורה? התשובה נעוצה בכך ש-JavaScript היא <strong>single threaded</strong>. בנקודה, אנחנו נרצה להציג בפניכם את הרעיון של <strong>threads</strong>.</p>

<h2 id="Threads">Threads</h2>

<p><strong>Thread</strong> הוא בעיקרון תהליך יחיד שתוכנית יכולה להשתמש בה על מנת להשלים משימות. כל תהליך כזה יכול לבצע משימה אחת בכל פעם: </p>

<pre>Task A --&gt; Task B --&gt; Task C</pre>

<p>
  כל משימה תרוץ באופן טורי, רציף, אך לא סימולטני. משימה צריכה להסתיים כדי שהבאה בתור תחל בריצה. </p>

<p>
  כפי שאמרנו מקודם, הרבה מחשבים הם בעלי מספר רב של ליבות, כך שהם יכולים לבצע מספר דברים בבת אחת. שפות תכנות שתומכות בליבות מרובות יכולות להתשמש בליבות אלו על מנת לבצע מספר פעולות באופן סימולטני ובמקביל: </p>

<pre>Thread 1: Task A --&gt; Task B
Thread 2: Task C --&gt; Task D</pre>

<h3 id="JavaScript_-_single_threaded">JavaScript -  single threaded</h3>

<p>JavaScript היא single-threaded.
  אפילו אם יש מספר ליבות, אנחנו יכולים רק להריץ משימה על thread יחיד, שנקרא <strong>main thread</strong>. הדוגמא שלנו מלמעלה מורצת כך: </p>

<pre>Main thread: Render circles to canvas --&gt; Display alert()</pre>

<p>
  לאחר זמן מסויים, JavaScript קיבלה מס׳ כלים על מנת להתמודד עם בעיות אלו.

  <a href="/en-US/docs/Web/API/Web_Workers_API">Web workers</a>
  מאפשר לנו לשלוח חלק מהביצוע של JavaScript ל-thread נפרד שנקרא בשם worker, כך שאנחנו יכולים להריץ קודי JavaScript באופן סימולטני מקביל. בדרך כלל אנחנו נשתמש ב-worker להריץ תהליכי

  allow you to send some of the JavaScript processing off to a separate thread, called a worker, so that you can run multiple JavaScript chunks simultaneously. You'd generally use a worker to run expensive processes off the main thread so that user interaction is not blocked.</p>

<pre>  Main thread: Task A --&gt; Task C
Worker thread: Expensive task B</pre>

<p>
  כעת, לאחר שאנחנו יודעים זאת, נסתכל על  <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/simple-sync-worker.html">simple-sync-worker.html</a> או <a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync-worker.html">כדף אינטרנט</a>, ונפתח את הקונסולה.
  בקובץ זה כתבנו מחדש את הדוגמא שלנו לחישוב 10 מיליון תאריכים בworker thread נפרד. כעת כשאנחנו לוחצים על הכפתור, הדפדן מסוכל להציג את הפסקה לפני שחישוב התאיריכים הסתיימו. הפעולה הראשונה כבר לא חוסמת את הפעולה השנייה.

  </p>

<h2 id="Asynchronous_code_קוד_א-סינכרוני">Asynchronous code קוד א-סינכרוני</h2>

<p>Web workers הם מאוד שימושיים, אבל יש להם הגבלות.
  אחת מהן היא שהם לא יכולים לגשת ל-{{Glossary("DOM")}} - אנחנו לא יכולים לגרום ל-worker לעשות שום דבר שיכול לעדכן את ממשק המשתמש, את ה-UI. אנחנו לא נוכל לעבד מיליון כדורים כחולים בתוך ה-worker שלנו. הוא בעיקרון יכול רק לעשות מניפולציות מספריות.
</p>

<p>
  הבעיה השנייה היא שלמרות שהקוד שרץ בתוך ה-000000 לא חוסם, הוא עדיין בסופו של דבר סינכרוני. זה הופך לבעיה כשאר פונקציה מתבססת על התוצאה של חישובים של תהליכים שקדמו לפונקציה. הסתכלו על התרשים הבא:</p>

<pre>Main thread: Task A --&gt; Task B</pre>

<p>
  במקרה זה, משימה A עושה משהו כמו הבאת תמונה מהשרת, ואז משימה B עושה משהו עם התמונה הזו, כגון החלת פילטר. אם אנחנו נתחיל את משימה A ואז ישר נריץ את משימה B , אנחנו נקבל שגיאה כי התמנוה עדיין לא זמינה, כי משימה A לא הסתיימה עדיין. </p>

<pre>  Main thread: Task A --&gt; Task B --&gt; |Task D|
Worker thread: Task C -----------&gt; |      |</pre>

<p>
  במקרה הזה לדוגמא, יש לנו את משימה D שמבצעת שימוש בתוצאות של משימה B ומשימה C. אם אנחנו יכולים להבטיח שהתוצאות של שתי משימות אלו יהיהו זמינות בואתו הזמן, אז זה יכול להיות תקין, אבל זה בדרך כלל לא קורה. אם משימה D תנסה לרוץ לפני שהערכים שהיא צריכה לקבל זמינים עבורה, זה יחזיר לנו רוב הסיכויים שגיאה.</p>

<p>
  על מנת לתקן בעיות שכאלו, דפדנים מאפשרים לנו להריץ מס׳ פעולות באופן א-סינכרוני. תכונות כמו <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a> מאפשרות לנו לקבוע משימה שתרוץ, כמו לדוגמא הבאת תמונה מהשרת, ולחכות עד אשר התוצאה של אותה משימה תוחזר שנריץ פעולה מסויימת אחרת.</p>

<pre>Main thread: Task A                   Task B
    Promise:      |__async operation__|</pre>

<p>
  מאחר שהפעולה מתרחשת במקום אחר, ה-main thread לא חסום בזמן שהפעולה הא-סינכרונית מתרחשת.

</p>

<p>
  אנחנו נסתכל כיצד אנחנו יכולים לרשום קוד א-סינכרוני במאמר הבא.
  </p>

<h2 id="לסיכום">לסיכום</h2>

<p>
    עיצוב תוכנה מודרני סובב יותר ויותר סביב שימוש בתכנות א-סינכרוני, כדי לאפשר לתוכניות לעשות יותר מדבר אחד בכל פעם. כאשר נשתמש ב-API חדשים ובעלי יכולות מתקדמות, אנחנו נמצא יותר מקרים שבהם הדרך היחידה לבצע משהו מסויים היא רק באופן א-סינכרוני. זה היה דיי קשה בעבר לכתוב קוד א-סינכרוני. אמנם עדיין לוקח זמן להתרגל לכך, אבל זה נעשה הרבה יותר קל. ביתר המאמרים במודול זה אנחנו נסביר מדוע קוד א-סינכרוני הוא רלוונטי וחשוב וכיצד נעצב את הקוד על מנת להימנע מחלק מהבעיות שסקרנו במאמר זה.


    יותר מדבר אחד בכל פעם. כשאתה משתמש בממשקי API חדשים וחזקים יותר, תמצא מקרים נוספים שבהם הדרך היחידה לעשות דברים היא בצורה אסינכרונית. פעם היה קשה לכתוב קוד אסינכרוני. זה עדיין לוקח להתרגל, אבל זה נעשה הרבה יותר קל. בשאר מודול זה נבדוק יותר מדוע חשוב קוד אסינכרוני וכיצד לתכנן קוד שנמנע מכמה מהבעיות שתוארו לעיל.</p>

<h2 id="במודול_זה">במודול זה</h2>

<ul>
 <li><a href="/he/docs/Learn/JavaScript/Asynchronous/Concepts">עקרונות תכנות א-סינכרוני כלליים</a></li>
 <li><a href="/he/docs/Learn/JavaScript/Asynchronous/Introducing">הצגת asynchronous JavaScript</a></li>
 <li><a href="/he/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">Asynchronous JavaScript: שימוש ב-Intervals ו-Timeouts</a></li>
 <li><a href="/he/docs/Learn/JavaScript/Asynchronous/Promises">טיפול בפעולות א-סינכרוניות באמצעות Promises</a></li>
 <li><a href="/he/docs/Learn/JavaScript/Asynchronous/Async_await">הפיכת Asynchronous Programming לקל יותר עם async ועם await</a></li>
 <li><a href="/he/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">בחירת האפשרות המתאימה</a></li>
</ul>