-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy path11_async.html
More file actions
682 lines (464 loc) · 135 KB
/
11_async.html
File metadata and controls
682 lines (464 loc) · 135 KB
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
<!doctype html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>برنامهنویسی ناهمگام :: Eloquent JavaScript</title>
<link rel=stylesheet href="js/node_modules/codemirror/lib/codemirror.css">
<script src="js/acorn_codemirror.js"></script>
<link rel=stylesheet href="css/ejs.css">
<script src="js/sandbox.js"></script>
<script src="js/ejs.js"></script><script>var chapNum = 11;var sandboxLoadFiles = ["code/crow-tech.js","code/chapter/11_async.js"];</script><script>var clicky_site_ids = clicky_site_ids || []; clicky_site_ids.push(101171577);</script>
<script async src="//static.getclicky.com/js"></script>
<script id='pixel-script-poptin' src='https://cdn.popt.in/pixel.js?id=ea6051b792008' async='true'></script>
</head>
<article>
<nav><a href="10_modules.html" title="previous chapter">◀</a> <a href="index.html" title="cover">◆</a> <a href="12_language.html" title="next chapter">▶</a></nav>
<h1><span class=chap_num>فصل 11</span>برنامهنویسی ناهمگام</h1>
<blockquote>
<p><a class="p_ident" id="p_2jmj7l5rSw" href="#p_2jmj7l5rSw" tabindex="-1" role="presentation"></a>چه کسی میتواند تا تهنشین شدن گلهای آب، شکیبا باشد؟ چهکسی میتواند بیحرکت بماند تا لحظهی درست عمل فرا برسد؟</p>
<footer>لائودْزی, <cite>دائو ده جینگ</cite></footer>
</blockquote><figure class="chapter framed"><img src="img/chapter_picture_11.jpg" alt="Picture of two crows on a branch"></figure>
<p><a class="p_ident" id="p_2jmj7l5rSw_1" href="#p_2jmj7l5rSw_1" tabindex="-1" role="presentation"></a>بخش مرکزی یک کامپیوتر، بخشی که گامهای اجرای برنامهی ما را برمیدارد، <em>پردازشگر</em> نامیده می شود. برنامههایی که تا کنون دیدهایم از نوعی بوده اند که پردازشگر را تا وقتی که کارشان تمام شود، مشغول نگه می دارند. سرعت اجرا در چیزی مثل یک حلقه که با اعداد سر و کار دارد به میزان زیادی به سرعت پردازشگر ارتباط دارد.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_2" href="#p_2jmj7l5rSw_2" tabindex="-1" role="presentation"></a>اما خیلی از برنامهها، با چیزهایی غیر از پردازشگر تعامل دارند. به عنوان مثال، ممکن است با کامپیوتری در یک شبکه تعامل داشته باشند یا دادهها را از دیسک سخت بخوانند – که خیلی کندتر از گرفتن آن ها از حافظهی اصلی است.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_3" href="#p_2jmj7l5rSw_3" tabindex="-1" role="presentation"></a>زمانی که اتفاقاتی این چنینی می افتد، بی کار گذاشتن پردازشگر کار نادرستی است- ممکن است کارهای دیگری وجود داشته باشد که بتوان در آن حین انجام داد. این کار تا حدی توسط سیستم عامل مدیریت میشود که پردازشگر را بین برنامههای متعددی که در حال اجرا هستند بکار میگیرد. اما در مواقعی که میخواهیم یک برنامهی واحد بتواند در هنگام انتظار برای یک درخواست شبکه به اجرا و پیشرفت خود ادامه دهد، از سیستمعامل کمکی بر نمیآید.</p>
<h2><a class="h_ident" id="h_1leuaMqErl" href="#h_1leuaMqErl" tabindex="-1" role="presentation"></a>ناهمگامی</h2>
<p><a class="p_ident" id="p_2jmj7l5rSw_4" href="#p_2jmj7l5rSw_4" tabindex="-1" role="presentation"></a>در یک مدل برنامهنویسی <em>همگام،</em> همه چیز یک به یک اتفاق می افتد. زمانی که تابعی را فراخوانی میکنید که عهدهدار اجرای کاری طولانی است، تنها بعد از اتمام آن کار و برگرداندن نتیجهی تابع، کنترل به برنامه برمیگردد. در حین اجرای تابع، برنامهی شما متوقف خواهد ماند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_5" href="#p_2jmj7l5rSw_5" tabindex="-1" role="presentation"></a>در مدل <em>ناهمگام</em> میتوان چندین کار را در یک زمان انجام داد. زمانی که کاری را شروع میکنید، برنامهی شما به اجرا ادامه خواهد داد. زمانی که آن کار تمام میشود، برنامه خبردار شده و به نتایج دست خواهد یافت (به عنوان مثال میتوان به خواندن اطلاعات از دیسک سخت اشاره کرد).</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_6" href="#p_2jmj7l5rSw_6" tabindex="-1" role="presentation"></a>میتوان برنامهنویسی همگام و ناهمگام را با یک مثال کوچک مقایسه کرد: برنامهای که از دو منبع در شبکه، اطلاعاتی دریافت میکند و بعد نتایج را با هم ترکیب میکند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_7" href="#p_2jmj7l5rSw_7" tabindex="-1" role="presentation"></a>در یک محیط همگام، جایی که درخواست فقط زمانی برمی گردد که کارش را تمام کرده باشد، آسان ترین روش انجام این کار ارسال درخواست ها یکی پس از دیگری است. مشکل این روش این است که درخواست دوم زمانی شروع میشود که درخواست اول تمام شده باشد. جمع زمانی که صرف میشود حداقل برابر است با مجموع زمان پاسخهای درخواست ها.</p>
<p><a class="p_ident" id="p_3SG8LH3yFM" href="#p_3SG8LH3yFM" tabindex="-1" role="presentation"></a>راه حل این مسئله، در یک سیستم همگام، استفاده از نخهای (threads) اضافی کنترل است. یک <em>thread</em> یک برنامهی دیگر است که در حال اجرا است که اجرای آن ممکن است توسط سیستم عامل بین برنامههای دیگر قرار گیرد – چون بیشتر کامپیوترهای مدرن دارای چندین پردازشگر هستند، چندین thread را میتوان در یک آن روی پردازشگرها اجرا کرد. یک thread دیگر میتواند درخواست دوم را شروع کند و سپس هر دوی thread ها منتظر نتیجهی درخواستشان می مانند که بعد از آن دوباره همگام شده و نتایج را باهم ترکیب می کنند.</p>
<p><a class="p_ident" id="p_woPjde2M6/" href="#p_woPjde2M6/" tabindex="-1" role="presentation"></a>در نمودار پیش رو، خطوط درشت نمایانگر زمانی است که برنامه سپری میکند تا در حالت نرمال اجرا شود، و خطوط باریک نشانگر زمانی است که برای پاسخ شبکه صرف میشود. در مدل همگام، زمانی که توسط شبکه گرفته میشود به عنوان بخشی از جدول زمانی برای thread داده شده محسوب میشود. در مدل ناهمگام، شروع یک عملیات مرتبط با شبکه، به طور مفهومی باعث ایجاد یک انشعاب در جدول زمانی میشود. برنامهای که این انشعاب را شروع کرده است به اجرای خود ادامه می دهد، و آن عملیات به موازات آن انجام میشود و وقتی پایان یافت برنامه را باخبر میکند.</p><figure><img src="img/control-io.svg" alt="Control flow for synchronous and asynchronous programming"></figure>
<p><a class="p_ident" id="p_2jmj7l5rSw_8" href="#p_2jmj7l5rSw_8" tabindex="-1" role="presentation"></a>راه دیگری که میتوان با آن تفاوت این دو را بیان کرد این است که در مدل همگام، انتظار برای پایان درخواستها به صورت ضمنی است در حالیکه در مدل ناهمگام صریح و تحت کنترل ما میباشد.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_9" href="#p_2jmj7l5rSw_9" tabindex="-1" role="presentation"></a>ناهمگامی مثل چاقوی دولبه است. برای برنامههایی که مناسب اجرای مستقیم خطی نیستند کار را ساده تر میکند اما در عین حال میتواند برای برنامههایی که به صورت مستقیم خطی اجرا میشوند نامناسب باشد. در ادامه این فصل با راههایی برای حل این ناهمگونی آشنا خواهیم شد.</p>
<p><a class="p_ident" id="p_DJSgOGbrf5" href="#p_DJSgOGbrf5" tabindex="-1" role="presentation"></a>هر دو پلتفرم مهم برنامهنویسی جاوااسکریپت – مرورگرها و <code><bdo></code>Node.js<code></bdo></code> – عملیاتی که ممکن است زمانگیر باشند را به صورت ناهمگام اجرا می کنند و از نخها (threads) استفاده نمی کنند. به دلیل اینکه برنامهنویسی روی thread ها کار سختی محسوب میشود (در این نوع برنامهنویسی درک کارکرد برنامه، به دلیل انجام چند کار در آن واحد بسیار سختتر میشود)، روش ناهمگام عموما چیز خوبی محسوب میشود.</p>
<h2><a class="h_ident" id="h_zUZJr7Lzut" href="#h_zUZJr7Lzut" tabindex="-1" role="presentation"></a>فناوری کلاغها</h2>
<p><a class="p_ident" id="p_2jmj7l5rSw_10" href="#p_2jmj7l5rSw_10" tabindex="-1" role="presentation"></a>خیلی از مردم میدانند که کلاغها پرندههایی بسیار باهوش هستند. آن ها می توانند از ابزار استفاده کنند، برای آینده برنامه ریزی کنند، چیزهایی را به خاطر بسپارند و حتی این موارد را با هم به اشتراک بگذارند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_11" href="#p_2jmj7l5rSw_11" tabindex="-1" role="presentation"></a>چیزی که بیشتر مردم از آن آگاه نیستند این است که کلاغها توانایی های زیادی دارند که از دید ما مخفی می کنند. یکی از متخصصین مشهور (و کمی عجیب و غریب) کلاغها به من گفت که فناوری کلاغ خیلی از فناوری انسان عقب نیست و به زودی به انسان ها میرسند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_12" href="#p_2jmj7l5rSw_12" tabindex="-1" role="presentation"></a>به عنوان مثال، نهادهای زیادی بین کلاغها وجود دارد که توانایی ساخت وسایل محاسباتی را دارند. این وسایل شبیه وسایل محاسباتی انسانها، الکترونیکی نیستند بلکه از رفتارهای حشراتی کوچک، گونههایی نزدیک به موریانه که یک رابطهی همزیستی با کلاغ ها توسعه داده اند، بهره برداری می کنند. کلاغها برایشان غذا فراهم می کنند و در عوض حشرات کلنیهای پیچیدهی آن ها را ساخته و بکار می اندازند، که به کمک موجودات زندهای که در درون آن ها زندگی می کنند، محاسبات را انجام میدهند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_13" href="#p_2jmj7l5rSw_13" tabindex="-1" role="presentation"></a>این گونه کلنیها معمولا در لانههای بزرگ و قدیمی قرار دارند. پرندهها و حشرات با همکاری هم شبکهای از ساختارهای گلی پیازیشکل را میسازند و بین ترکهای لانه پنهان می کنند که در آن حشرات زندگی و کار خواهند کرد.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_14" href="#p_2jmj7l5rSw_14" tabindex="-1" role="presentation"></a>برای تعامل با دیگر وسایل،این ماشینها از سیگنالهای نور استفاده می کنند. کلاغها قطعاتی از مواد انعکاسی را در ساقههای خاصی که برای ارتباط در نظر گرفته شده اند جاسازی می کنند و حشرات آن ها را هدف قرار میدهند تا نور را به لانهی دیگری بتابانند و دادهها را به صورت دنبالهای از چشمکهای کوتاه به رمز در می آورند. این یعنی فقط لانههایی که دارای یک ارتباط متصل بصری هستند میتوانند با یکدیگر تعامل کنند.</p>
<p><a class="p_ident" id="p_cCgxPRsF1V" href="#p_cCgxPRsF1V" tabindex="-1" role="presentation"></a>دوست متخصص کلاغ ما نقشهای از شبکهی لانههای کلاغها در روستای <code><bdo></code>Hières-sur-Amby<code></bdo></code> قرار دارد، کشیده است که در حاشیهی رودخانهی Rhône قرار دارد. آن نقشه نشان می دهد که لانهها و ارتباطاتشان چگونه است:</p><figure><img src="img/Hieres-sur-Amby.png" alt="A network of crow nests in a small village"></figure>
<p><a class="p_ident" id="p_2jmj7l5rSw_15" href="#p_2jmj7l5rSw_15" tabindex="-1" role="presentation"></a>در نمونهای شگفتانگیز از تکامل همگرا، کامپیوترهای کلاغها، جاوااسکریپت را اجرا می کنند. در این فصل قرار است بعضی از قابلیتهای پایهای شبکه را برایشان برنامهنویسی کنیم.</p>
<h2><a class="h_ident" id="h_mpRFz5EAjK" href="#h_mpRFz5EAjK" tabindex="-1" role="presentation"></a>توابع callback</h2>
<p><a class="p_ident" id="p_jU9HKeneWd" href="#p_jU9HKeneWd" tabindex="-1" role="presentation"></a>یکی از راههای برنامهنویسی ناهمگام این است توابعی که یک کار زمانگیر را انجام می دهند، یک آرگومان اضافی دریافت کنند، یک تابع callback. در این روش، تابع اصلی اجرا شده و پایان می پذیرد سپس تابع callback با نتایج دریافتی از تابع اصلی فراخوانی می گردد.</p>
<p><a class="p_ident" id="p_Kzh6PibMf8" href="#p_Kzh6PibMf8" tabindex="-1" role="presentation"></a>به عنوان یک مثال، تابع <code>setTimeout</code>، که در <code><bdo></code>Node.js<code></bdo></code> و مرورگرها در دسترس است، به اندازهی هزارم ثانیه ای که مشخص شده است منتظر می ماند و سپس یک تابع را فراخوانی میکند.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_RyFm7Uoiuv" href="#c_RyFm7Uoiuv" tabindex="-1" role="presentation"></a><span class="cm-variable">setTimeout</span>(() <span class="cm-operator">=></span> <span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Tick"</span>), <span class="cm-number">500</span>);</pre>
<p><a class="p_ident" id="p_2jmj7l5rSw_16" href="#p_2jmj7l5rSw_16" tabindex="-1" role="presentation"></a>این انتظار معمولا خیلی کاربردهای مهمی ندارد؛ اما گاهی تواند مفید باشد مانند بهروزرسانی یک انیمیشن یا بررسی طول کشیدن چیزی در برنامه.</p>
<p><a class="p_ident" id="p_tNWze/ephs" href="#p_tNWze/ephs" tabindex="-1" role="presentation"></a>اجرای چندین عمل ناهمگام در یک ردیف با استفاده از توابع callback به این معنا است که شما باید به ارسال توابع جدید برای ادامهی محاسبه بعد از هر عمل ادامه دهید.</p>
<p><a class="p_ident" id="p_tNWze/ephs_1" href="#p_tNWze/ephs_1" tabindex="-1" role="presentation"></a>بیشتر کامپیوترهای موجود در لانههای کلاغها، دارای یک بافت ذخیرهسازی بلند مدت میباشند، جاییکه اطلاعات، درون شاخهها حک میشوند و میتوان آن ها را بعدا دوباره خواند. حک کردن یا پیدا کردن یک بخش از اطلاعات زمانگیر است بنابراین رابط سیستم ذخیرهسازی بلند مدت، ناهمگام خواهد بود و از توابع callback استفاده خواهد شد.</p>
<p><a class="p_ident" id="p_KiwPWHDFBj" href="#p_KiwPWHDFBj" tabindex="-1" role="presentation"></a>بافتهای ذخیرهسازی، بخشهای اطلاعات را که به فرمت JSON درآمده اند، تحت نامهایی ذخیره می کنند. یک کلاغ ممکن است اطلاعاتی در مورد مخفیگاه غذاها را به عنوان <code><bdo></code><code>"food caches"</code><code></bdo></code> ذخیره کند، که میتواند دارای آرایهای از نامهایی باشد که به دیگر بخشهای اطلاعات اشاره می نمایند، اطلاعاتی که مخفیگاه واقعی را توصیف می کنند. برای جستجوی یک مخفیگاه غذا در بافتهای ذخیرهسازی لانهی <code><bdo></code>Big Oak<code></bdo></code>، یک کلاغ میتواند کدی مثل زیر را اجرا کند.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_f7XSZ4G+k8" href="#c_f7XSZ4G+k8" tabindex="-1" role="presentation"></a><span class="cm-keyword">import</span> {<span class="cm-def">bigOak</span>} <span class="cm-keyword">from</span> <span class="cm-string">"./crow-tech"</span>;
<span class="cm-variable">bigOak</span>.<span class="cm-property">readStorage</span>(<span class="cm-string">"food caches"</span>, <span class="cm-def">caches</span> <span class="cm-operator">=></span> {
<span class="cm-keyword">let</span> <span class="cm-def">firstCache</span> <span class="cm-operator">=</span> <span class="cm-variable-2">caches</span>[<span class="cm-number">0</span>];
<span class="cm-variable">bigOak</span>.<span class="cm-property">readStorage</span>(<span class="cm-variable-2">firstCache</span>, <span class="cm-def">info</span> <span class="cm-operator">=></span> {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable-2">info</span>);
});
});</pre>
<p><a class="p_ident" id="p_2jmj7l5rSw_17" href="#p_2jmj7l5rSw_17" tabindex="-1" role="presentation"></a>(تمامی متغیرها و رشتهها از زبان کلاغی به زبان انگلیسی ترجمه شده اند.)</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_18" href="#p_2jmj7l5rSw_18" tabindex="-1" role="presentation"></a>این سبک از برنامهنویسی شدنی است؛ اما با هر بار عمل ناهمگام، میزان تورفتگی اضافه میشود ،چرا که به یک تابع دیگر نیاز خواهد بود. برای انجام کارهای پیچیدهتر ، مثل انجام چند عمل در یک زمان واحد، این شیوهی کدنویسی میتواند کمی بدقواره و نامناسب شود.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_19" href="#p_2jmj7l5rSw_19" tabindex="-1" role="presentation"></a>کامپیوترهای لانهها، طوری ساخته شده اند که بتوانند به وسیلهی جفتهای درخواست-پاسخ با هم ارتباط برقرار کنند. این یعنی یک لانه، پیامی را به لانهی دیگری ارسال میکند، که این لانه نیز بلافاصله پیامی را که حاوی تایید دریافت و احتمالا شامل پاسخی به درخواست است برمیگرداند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_20" href="#p_2jmj7l5rSw_20" tabindex="-1" role="presentation"></a>هر پیغام توسط یک <em>نوع</em>، برچسب گذاری میشود که تعیین کنندهی نحوهی مدیریت آن میباشد. کد ما میتواند توابعی را برای رسیدگی به انواع خاص، تعریف کند، و زمانی که درخواستی از آن نوع آمد ، تابع رسیدگیکننده فراخوانی شده تا پاسخی را تولید کند.</p>
<p><a class="p_ident" id="p_tNWze/ephs_2" href="#p_tNWze/ephs_2" tabindex="-1" role="presentation"></a>رابطی که توسط ماژول <code><bdo></code><code>"./<wbr>crow-tech"</code><code></bdo></code> صادر میشود، تابعی دارای callback برای تعامل فراهم میکند. لانهها دارای متدی به نام <code>send</code> هستند که درخواستها را ارسال میکند. این متد نام لانهی مقصد، نوع درخواست و محتوای درخواست را به عنوان سه آرگومان اول گرفته و آرگومان بعدی یک تابع است که زمانی که یک پاسخ دریافت می شود، فراخوانی میشود.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_qzWIKDLKSP" href="#c_qzWIKDLKSP" tabindex="-1" role="presentation"></a><span class="cm-variable">bigOak</span>.<span class="cm-property">send</span>(<span class="cm-string">"Cow Pasture"</span>, <span class="cm-string">"note"</span>, <span class="cm-string">"Let's caw loudly at 7PM"</span>,
() <span class="cm-operator">=></span> <span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Note delivered."</span>));</pre>
<p><a class="p_ident" id="p_2jmj7l5rSw_21" href="#p_2jmj7l5rSw_21" tabindex="-1" role="presentation"></a>اما برای اینکه لانهها را قادر سازیم تا آن درخواست را دریافت کند، میبایست ابتدا نوع درخواستی به نام <code>"note"</code> را تعریف کنیم. کدی که به این درخواستها رسیدگی میکند باید نه تنها بر روی کامپیوتر این لانه اجرا شود بلکه باید روی تمامی لانههایی که میتوانند پیامی از این نوع را دریافت کنند اجرا شود. ما فرض می کنیم که کلاغی پرواز کرده و کد ما را روی همهی لانهها نصب میکند.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_38THPHvc7d" href="#c_38THPHvc7d" tabindex="-1" role="presentation"></a><span class="cm-keyword">import</span> {<span class="cm-def">defineRequestType</span>} <span class="cm-keyword">from</span> <span class="cm-string">"./crow-tech"</span>;
<span class="cm-variable">defineRequestType</span>(<span class="cm-string">"note"</span>, (<span class="cm-def">nest</span>, <span class="cm-def">content</span>, <span class="cm-def">source</span>, <span class="cm-def">done</span>) <span class="cm-operator">=></span> {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`${</span><span class="cm-variable-2">nest</span>.<span class="cm-property">name</span><span class="cm-string-2">}</span> <span class="cm-string-2">received note: ${</span><span class="cm-variable-2">content</span><span class="cm-string-2">}</span><span class="cm-string-2">`</span>);
<span class="cm-variable-2">done</span>();
});</pre>
<p><a class="p_ident" id="p_2jmj7l5rSw_22" href="#p_2jmj7l5rSw_22" tabindex="-1" role="presentation"></a>تابع <code>defineRequestType</code> یک نوع درخواست جدید را تعریف میکند. در مثال، امکان پشتیبانی از درخواستهای <code>"note"</code> اضافه میشود که در واقع تنها یک یادداشت را به لانهی داده شده ارسال میکند. پیادهسازی ما، از <code><bdo></code><code>console.log</code><code></bdo></code> برای تایید رسیدن درخواست استفاده میکند. لانهها دارای خاصیتی به نام <code>name</code> هستند که نامشان را نگه داری میکند.</p>
<p><a class="p_ident" id="p_lEh6rApDCd" href="#p_lEh6rApDCd" tabindex="-1" role="presentation"></a>آرگومان چهارمی که به تابع رسیدگی کننده داده میشود، <code>done</code>، یک تابع callback است که باید زمانی که درخواست کارش تمام شد فراخوانی شود. اگر از مقدار بازگشتی (توسط return) از تابع رسیدگی کننده به عنوان مقدار پاسخ استفاده کرده بودیم ، در اینصورت رسیدگیکنندهی درخواست نمیتوانست خودش یک عمل ناهمگام را اجرا کند. تابعی که یک کار ناهمگام را انجام می دهد نوعا قبل از انجام آن کار برمیگردد و برای اجرای تابع callback پس از انجام کار تنظیم میشود. بنابراین ما نیاز به مکانیزمهایی ناهمگام داریم – در این مثال، به یک تابع callback دیگر- تا وقتی که یک پاسخ آماده بود، علامت بدهیم.</p>
<p><a class="p_ident" id="p_jU9HKeneWd_1" href="#p_jU9HKeneWd_1" tabindex="-1" role="presentation"></a>به شکلی، ناهمگامی مسری است. هر تابعی که یک تابع را فراخوانی کند که به صورت ناهمگام عمل میکند، خودش باید ناهمگام باشد که میتوان با استفاده از یک callback یا مکانیزمی شبیه به آن باشد تا نتیجهاش را تحویل دهد. فراخوانی یک callback، از برگرداندن یک مقدار به شکل ساده، کمی پیچیده تر و مشکلساز تر است؛ بنابراین استفاده از این روش برای ساختاردهی بخشهای بزرگی از برنامهتان جالب نیست.</p>
<h2><a class="h_ident" id="h_i7gRmvd0TO" href="#h_i7gRmvd0TO" tabindex="-1" role="presentation"></a>Promise ها</h2>
<p><a class="p_ident" id="p_2jmj7l5rSw_23" href="#p_2jmj7l5rSw_23" tabindex="-1" role="presentation"></a>اگر بتوان مفاهیم مجرد را به صورت مقدارها نمایش داد، اغلب درکشان ساده تر میشود. در رابطه با کارهای ناهمگام میتوانید به جای تنظیم یک تابع برای فراخوانی در یک نقطهی خاص در آینده، یک شیء را برگردانید که این رخداد آینده را نمایندگی کند.</p>
<p><a class="p_ident" id="p_jC/YRC/GWd" href="#p_jC/YRC/GWd" tabindex="-1" role="presentation"></a>این دقیقا چیزی است که کلاس استاندارد <code>Promise</code> انجام می دهد. یک <em>promise</em> یک عمل ناهمگام است که در زمانی تکمیل میشود و مقداری را تولید میکند. میتواند هرکسی که علاقمند باشد را در زمان آماده شدن مقدارش باخبر کند.</p>
<p><a class="p_ident" id="p_rAjGu7aKrd" href="#p_rAjGu7aKrd" tabindex="-1" role="presentation"></a>آسان ترین روش ایجاد یک promise فراخوانی <code><bdo></code><code>Promise.resolve</code><code></bdo></code> است. این تابع اطمینان حاصل میکند که مقداری که به آن می دهید درون یک promise قرار میگیرد. اگر خودش از قبل یک promise بود، برگردانده می شود – در غیر این صورت، شما promise جدیدی دریافت میکنید که با مقدار شما به عنوان نتیجهاش بلافاصله پایان میپذیرد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_fzJ7VLwQ/i" href="#c_fzJ7VLwQ/i" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">fifteen</span> <span class="cm-operator">=</span> <span class="cm-variable">Promise</span>.<span class="cm-property">resolve</span>(<span class="cm-number">15</span>);
<span class="cm-variable">fifteen</span>.<span class="cm-property">then</span>(<span class="cm-def">value</span> <span class="cm-operator">=></span> <span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`Got ${</span><span class="cm-variable-2">value</span><span class="cm-string-2">}</span><span class="cm-string-2">`</span>));
<span class="cm-comment">// → Got 15</span></pre>
<p><a class="p_ident" id="p_Q87nCNSnKu" href="#p_Q87nCNSnKu" tabindex="-1" role="presentation"></a>برای گرفتن نتیجهی یک promise، میتوانید از متد <code>then</code> آن استفاده کنید. این متد تابع callbackای را ثبت میکند که در هنگامی که promise به نتیجه رسید و مقداری را تولید کرد، فراخوانی میشود. میتوانید چندین تابع callback را به یک promise اضافه کنید، و همهی آنها فراخوانی خواهند شد، حتی اگر آنها را بعد از به نتیجهرسیدن promise اضافه کنید.</p>
<p><a class="p_ident" id="p_J2+Ok30Bhh" href="#p_J2+Ok30Bhh" tabindex="-1" role="presentation"></a>اما این همهی آن چیزی نیست که متد <code>then</code> انجام می دهد. این متد promise دیگری را برمیگرداند، که مقداری که از تابع رسیدگی کننده برمی گردد را (resolve) را نتیجهیابی میکند یا اگر یک promise را برگرداند، برای آن promise منتظر می ماند سپس به حل و فصل نتیجهاش می پردازد.</p>
<p><a class="p_ident" id="p_x1wxB3v/ex" href="#p_x1wxB3v/ex" tabindex="-1" role="presentation"></a>خوب است که promiseها را به عنوان وسایلی که مقدارها را به درون فضای ناهمگام انتقال میدهند تصور کنید. یک مقدار نرمال به صورت طبیعی وجود دارد. یک مقدار وعده داده شده (promised value) مقداری است که ممکن است از قبل وجود داشته باشد یا در نقطهای در آینده ظاهر شود. محاسباتی که به عنوان promise تعریف میشوند روی این گونه مقدارها عمل می کنند و همزمان با در دسترس قرار گرفتن مقدارها به اجرا در می آیند.</p>
<p><a class="p_ident" id="p_CZ6CO6EnK8" href="#p_CZ6CO6EnK8" tabindex="-1" role="presentation"></a>برای ایجاد یک promise، میتوانید از <code>Promise</code> به عنوان یک سازنده استفاده کنید. رابط آن کمی غیرمعمول است – سازنده، یک تابع را به عنوان آرگومان میگیرد که آن را بلافاصله فراخوانی میکند و تابعی به آن ارسال میکند که این تابع میتواند برای نتیجه یابی promise استفاده شود. این سازنده به صورتی که گفته شد کار میکند نه مثلا به شکلی که با یک متد <code>resolve</code> کار کند و فقط کدی که promise را ایجاد کرده بتواند آن را نتیجهیابی کند.</p>
<p><a class="p_ident" id="p_jC/YRC/GWd_1" href="#p_jC/YRC/GWd_1" tabindex="-1" role="presentation"></a>این روشی است که میتوانید برای ایجاد یک رابط مبتنی بر promise برای تابع <code>readStorage</code> ایجاد کنید:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_gYXlRtzMyd" href="#c_gYXlRtzMyd" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">storage</span>(<span class="cm-def">nest</span>, <span class="cm-def">name</span>) {
<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">Promise</span>(<span class="cm-def">resolve</span> <span class="cm-operator">=></span> {
<span class="cm-variable-2">nest</span>.<span class="cm-property">readStorage</span>(<span class="cm-variable-2">name</span>, <span class="cm-def">result</span> <span class="cm-operator">=></span> <span class="cm-variable-2">resolve</span>(<span class="cm-variable-2">result</span>));
});
}
<span class="cm-variable">storage</span>(<span class="cm-variable">bigOak</span>, <span class="cm-string">"enemies"</span>)
.<span class="cm-property">then</span>(<span class="cm-def">value</span> <span class="cm-operator">=></span> <span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Got"</span>, <span class="cm-variable-2">value</span>));</pre>
<p><a class="p_ident" id="p_AJNl7eWKVe" href="#p_AJNl7eWKVe" tabindex="-1" role="presentation"></a>این تابع ناهمگام یک مقدار معنادار را تولید میکند. این مزیت اصلی promise ها است – آن ها استفاده از توابع ناهمگام را ساده می کنند. به جای اینکه مجبور باشیم callbackهای متعددی ارسال کنیم، توابع مبتنی بر promise شبیه توابع معمولی به نظر می رسند: ورودی ها را به عنوان آرگومان می گیرند و خروجی شان را تولید می کنند. تنها تفاوت این است که خروجی ممکن است هنوز در دسترس نباشد.</p>
<h2><a class="h_ident" id="h_C1ZTsB+NFB" href="#h_C1ZTsB+NFB" tabindex="-1" role="presentation"></a>شکست</h2>
<p><a class="p_ident" id="p_2jmj7l5rSw_24" href="#p_2jmj7l5rSw_24" tabindex="-1" role="presentation"></a>محاسبات عادی جاوااسکریپت میتوانند با شکست روبرو شده و یک استثنا را تولید کنند. محاسبات ناهمگام هم اغلب به چیزی شبیه به آن نیاز دارند. ممکن است یک درخواست در شبکه با شکست روبرو شود یا کدی که بخشی از یک محاسبهی ناهمگام است استثنایی را تولید کند.</p>
<p><a class="p_ident" id="p_jU9HKeneWd_2" href="#p_jU9HKeneWd_2" tabindex="-1" role="presentation"></a>یکی از حیاتیترین مشکلاتی که در سبک مبتنی بر callback برنامهنویسی ناهمگام وجود دارد این است که در این سبک، گزارش صحیح شکستها به توابع callback بسیار دشوار است.</p>
<p><a class="p_ident" id="p_jU9HKeneWd_3" href="#p_jU9HKeneWd_3" tabindex="-1" role="presentation"></a>یکی از راه حل های رایج برای آن این است که آرگومان اول callback را برای مشخص کردن شکست عمل در نظر می گیرند و دومین آرگومان، حاوی مقداری خواهد بود که در صورت موفقیت عمل، تولید میشود. این گونه توابع callback باید همیشه بررسی کنند که آیا استثنایی دریافت کرده اند یا خیر و اطمینان حاصل کنند که هر مشکلی که ایجاد می کنند، مانند استثناهای تولیدی توسط توابعی که فراخوانی میکنند، مدیریت شده و به تابع درستی داده میشود.</p>
<p><a class="p_ident" id="p_EbZSDdu4h/" href="#p_EbZSDdu4h/" tabindex="-1" role="presentation"></a>promiseها این کار را ساده تر کرده اند. میتوان آن ها را resolve (نتیجه یابی ) کرد (عمل با موفقیت به پایان رسیده) یا رد (reject) کرد (شکست خورده است). توابع رسیدگی کننده به موفقیت (که با متد <code>then</code> ثبت شده اند) فقط زمانی فراخوانی میشوند که عمل باموفقیت انجام شده باشد و rejectها به صورت خودکار به یک promise جدید سپرده میشوند که توسط <code>then</code> برگردانده میشود. و زمانی که یک تابع گرداننده (handler) استثنا تولید میکند، این به طور خودکار سبب میشود که promise ای که توسط فراخوانی متد thenاش تولید شده است رد بشود. بنابراین اگر یکی از عناصری که در زنجیرهی اعمال ناهمگام قرار دارد با شکست روبرو شود، خروجی تمام زنجیره به عنوان “رد شده” یا rejected در نظر گرفته میشود، و هیچ تابع گردانندهی دیگری بعد از نقطهای که با مشکل روبرو شده است فراخوانی نمیشود.</p>
<p><a class="p_ident" id="p_rAjGu7aKrd_1" href="#p_rAjGu7aKrd_1" tabindex="-1" role="presentation"></a>بسیار شبیه به نتیجهیابی یک promise که مقداری را فراهم می ساخت، رد شدن آن نیز مقداری را فراهم میکند، که معمولا به عنوان دلیل رد شدن شناخته میشود. زمانی که یک استثنا در یک تابع گرداننده باعث رد شدن میشود، مقدار استثنا به عنوان دلیل استفاده میشود. به طور مشابه زمانی که یک گرداننده، یک promise را برمی گرداند که رد شده است، این پذیرفتهنشدن به درون promise بعدی جریان می یابد. تابعی به نام <code><bdo></code><code>Promise.reject</code><code></bdo></code> وجود دارد که یک promise رد شده جدید بلافاصله ایجاد میکند.</p>
<p><a class="p_ident" id="p_rAjGu7aKrd_2" href="#p_rAjGu7aKrd_2" tabindex="-1" role="presentation"></a>رای رسیدگی صریح به این گونه رد شدنها، promise ها دارای متدی به نام <code>catch</code> هستند که یک گرداننده را برای فراخوانی در هنگام رد شدن ثبت میکند، شبیه به گردانندههای <code>then</code> که در موارد یافتن نتیجه صحیح استفاده می شدند. از این لحاظ نیز بسیار شبیه به <code>then</code> است که یک promise جدید برمی گرداند که در صورت نتیجهیابی بدون مشکل به نتیجهی promise اصلی منجر میشود و در غیر این صورت به نتیجهی گردانندهی <code>catch</code>. اگر یک گردانندهی <code>catch</code> خطایی تولید کند، promise جدید نیز رد میشود.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_25" href="#p_2jmj7l5rSw_25" tabindex="-1" role="presentation"></a>به عنوان یک راه خلاصه تر، متد <code>then</code> همچنین یک گردانندهی عدم پذیرش نیز به عنوان آرگومان دوم قبول میکند، بنابراین میتوانید هر دوی گردانندهها را با یک فراخوانی متد ثبت کنید.</p>
<p><a class="p_ident" id="p_r46r7g/dRF" href="#p_r46r7g/dRF" tabindex="-1" role="presentation"></a>تابعی که به سازندهی <code>Promise</code> ارسال میشود در کنار تابع موفقیت (resolve) آرگومان دومی را دریافت میکند، که میتواند برای رد کردن promise جدید استفاده شود.</p>
<p><a class="p_ident" id="p_CZ6CO6EnK8_1" href="#p_CZ6CO6EnK8_1" tabindex="-1" role="presentation"></a>زنجیرهی مقدارهای promise که با فراخوانیهایی که به <code>then</code> و <code>catch</code> زده شده است تولید شده را میتوان به عنوان یک خط لوله در طول مقدارهای ناهمگام یا حرکتهای منجر به شکست دانست. به دلیل این که این زنجیره به وسیلهی ثبت گردانندهها تولید میشود، هر پیوند دارای یک گردانندهی موفقیت یا عدم پذیرش (یا هر دو) است که به آن ارتباط دارد. گردانندههایی که تطبیقی با نوع خروجی (موفقیت یا شکست) ندارند در نظر گرفته نمیشوند. اما آن هایی که هماهنگ هستند فراخوانی میشوند و خروجی آن ها مشخص می کند چه نوع مقداری در ادامه خواهد آمد – موفقیت در زمانی که یک مقدار غیر promise بر می گرداند، عدم پذیرش زمانی که یک استثنا تولید میشود، و خروجی یک promise زمانی که یکی از آن ها را بر می گرداند.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_TOwOnoqIIW" href="#c_TOwOnoqIIW" tabindex="-1" role="presentation"></a><span class="cm-keyword">new</span> <span class="cm-variable">Promise</span>((<span class="cm-def">_</span>, <span class="cm-def">reject</span>) <span class="cm-operator">=></span> <span class="cm-variable-2">reject</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Error</span>(<span class="cm-string">"Fail"</span>)))
.<span class="cm-property">then</span>(<span class="cm-def">value</span> <span class="cm-operator">=></span> <span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Handler 1"</span>))
.<span class="cm-property">catch</span>(<span class="cm-def">reason</span> <span class="cm-operator">=></span> {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Caught failure "</span> <span class="cm-operator">+</span> <span class="cm-variable-2">reason</span>);
<span class="cm-keyword">return</span> <span class="cm-string">"nothing"</span>;
})
.<span class="cm-property">then</span>(<span class="cm-def">value</span> <span class="cm-operator">=></span> <span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Handler 2"</span>, <span class="cm-variable-2">value</span>));
<span class="cm-comment">// → Caught failure Error: Fail</span>
<span class="cm-comment">// → Handler 2 nothing</span></pre>
<p><a class="p_ident" id="p_jC/YRC/GWd_2" href="#p_jC/YRC/GWd_2" tabindex="-1" role="presentation"></a>بسیار شبیه به یک استثنای مدیریت نشده که توسط محیط رسیدگی میشود ، محیط های جاوااسکریپت میتوانند تشخیص دهند در چه زمانی یک عدم موفقیت promise رسیدگی نشده است و آن را به عنوان یک خطا گزارش خواهند داد.</p>
<h2><a class="h_ident" id="h_GQZBOQOHOg" href="#h_GQZBOQOHOg" tabindex="-1" role="presentation"></a>شبکهها دشوار هستند</h2>
<p><a class="p_ident" id="p_2jmj7l5rSw_26" href="#p_2jmj7l5rSw_26" tabindex="-1" role="presentation"></a>گاهی اوقات، نور کافی برای سیستم انعکاس نور کلاغها بهمنظور انتقال سیگنال وجود ندارد، یا چیزی مسیر سیگنال را مسدود کرده است. ممکن است سیگنالی فرستاده شود ولی هرگز دریافت نشود.</p>
<p><a class="p_ident" id="p_tNWze/ephs_3" href="#p_tNWze/ephs_3" tabindex="-1" role="presentation"></a>در این صورت، این باعث میشود که تابع callback ای که به متد <code>send</code> داده شده است هرگز فراخوانی نشود، که احتمالا موجب توقف برنامه بدون هیچ گونه اعلام مشکل میشود. خوب بود اگر بعد از یک دورهی زمانی مشخص که پاسخی دریافت نشد، یک درخواست به صورت خودکار منقضی می شد و یک شکست گزارش می شد.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_27" href="#p_2jmj7l5rSw_27" tabindex="-1" role="presentation"></a>اغلب، شکست های مربوط به ارسال به صورت تصادفی اتفاق می افتند، مانند بروز تداخل بین چراغ جلوی یک خودرو با سیگنالهای نوری، و در این صورت فقط دوباره فرستان درخواست مشکل را برطرف میکند. بنابراین هنگامی که هنوز در آن نقطه قرار داریم، اجازه بدهید تابع درخواست را طوری تنظیم کنیم که به طور خودکار قبل از اینکه دست از کار بکشد چندین بار درخواست را ارسال کند.</p>
<p><a class="p_ident" id="p_ycn/u9w/t9" href="#p_ycn/u9w/t9" tabindex="-1" role="presentation"></a>و به دلیل اینکه قبول کرده ایم که promise ها مفید هستند، پس تابع درخواست را تغییر می دهیم تا یک promise برگرداند. در رابطه با کاری که میتوانند انجام دهند تفاوتی بین callback ها و promise ها وجود ندارد. توابع مبتنی بر callback را میتوان پوشاند به شکلی که رابطی promise گونه داشته باشند و همین طور برعکس.</p>
<p><a class="p_ident" id="p_tNWze/ephs_4" href="#p_tNWze/ephs_4" tabindex="-1" role="presentation"></a>حتی زمانی که یک درخواست و پاسخ آن با موفقیت تحویل داده میشوند، پاسخ ممکن است نشانگر یک شکست باشد – به عنوان مثال، اگر درخواست تلاش کند که از نوع درخواستی استفاده کند که تعریف نشده است یا گرداننده یک خطا تولید کند. برای پشتیبانی از این، <code>send</code> و <code>defineRequestType</code> از قراردادی پیروی می کنند که قبل تر ذکر شد جاییکه اولین آرگومان فرستاده شده با تابع callback، دلیل شکست خواهد بود، در صورت وجود البته، و دومین آرگومان نتیجهی واقعی خواهد بود.</p>
<p><a class="p_ident" id="p_/HAGRiWyCN" href="#p_/HAGRiWyCN" tabindex="-1" role="presentation"></a>اینها را میتوان به وسیلهی یک پوشاننده (wrapper) به پذیرش و عدم پذیرش promise ترجمه کرد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Wg7xZ1po7J" href="#c_Wg7xZ1po7J" tabindex="-1" role="presentation"></a><span class="cm-keyword">class</span> <span class="cm-def">Timeout</span> <span class="cm-keyword">extends</span> <span class="cm-variable">Error</span> {}
<span class="cm-keyword">function</span> <span class="cm-def">request</span>(<span class="cm-def">nest</span>, <span class="cm-def">target</span>, <span class="cm-def">type</span>, <span class="cm-def">content</span>) {
<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">Promise</span>((<span class="cm-def">resolve</span>, <span class="cm-def">reject</span>) <span class="cm-operator">=></span> {
<span class="cm-keyword">let</span> <span class="cm-def">done</span> <span class="cm-operator">=</span> <span class="cm-atom">false</span>;
<span class="cm-keyword">function</span> <span class="cm-def">attempt</span>(<span class="cm-def">n</span>) {
<span class="cm-variable-2">nest</span>.<span class="cm-property">send</span>(<span class="cm-variable-2">target</span>, <span class="cm-variable-2">type</span>, <span class="cm-variable-2">content</span>, (<span class="cm-def">failed</span>, <span class="cm-def">value</span>) <span class="cm-operator">=></span> {
<span class="cm-variable-2">done</span> <span class="cm-operator">=</span> <span class="cm-atom">true</span>;
<span class="cm-keyword">if</span> (<span class="cm-variable-2">failed</span>) <span class="cm-variable-2">reject</span>(<span class="cm-variable-2">failed</span>);
<span class="cm-keyword">else</span> <span class="cm-variable-2">resolve</span>(<span class="cm-variable-2">value</span>);
});
<span class="cm-variable">setTimeout</span>(() <span class="cm-operator">=></span> {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">done</span>) <span class="cm-keyword">return</span>;
<span class="cm-keyword">else</span> <span class="cm-keyword">if</span> (<span class="cm-variable-2">n</span> <span class="cm-operator"><</span> <span class="cm-number">3</span>) <span class="cm-variable-2">attempt</span>(<span class="cm-variable-2">n</span> <span class="cm-operator">+</span> <span class="cm-number">1</span>);
<span class="cm-keyword">else</span> <span class="cm-variable-2">reject</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Timeout</span>(<span class="cm-string">"Timed out"</span>));
}, <span class="cm-number">250</span>);
}
<span class="cm-variable-2">attempt</span>(<span class="cm-number">1</span>);
});
}</pre>
<p><a class="p_ident" id="p_++U4Ouqvnt" href="#p_++U4Ouqvnt" tabindex="-1" role="presentation"></a>به دلیل اینکه promise ها میتوانند فقط یک بار موفق شوند (یا رد بشوند)، این روش کار خواهد کرد. اولین باری که <code>resolve</code> یا <code>reject</code> فراخوانی میشوند، خروجی promise را معین می کنند، و هر فراخوانی ای در بعد، مانند timeout که بعد از پایان درخواست می رسد یا درخواستی که بعد از یک پایان یک درخواست دیگر برمی گردد، در نظر گرفته نمی شوند.</p>
<p><a class="p_ident" id="p_rFYXmIrG7W" href="#p_rFYXmIrG7W" tabindex="-1" role="presentation"></a>برای ساخت یک حلقهی ناهمگام، برای تلاشهای اضافی، لازم است تا از یک تابع بازگشتی استفاده کنیم – یک حلقهی معمولی امکان توقف و صبر برای یک عمل ناهمگام را فراهم نمی کند. تابع <code>attemp</code> یک تلاش واحد برای ارسال یک درخواست ترتیب می دهد. همچنین یک زمان انقضا تنظیم میکند، اگر پاسخی بعد از 250 هزارم ثانیه نیامد ، یا تلاش بعد را شروع کند یا اگر این چهارمین تلاش بود ، promise را رد میکند و به عنوان دلیل عدم پذیرش هم یک نمونه از <code>Timeout</code> را استفاده میکند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_28" href="#p_2jmj7l5rSw_28" tabindex="-1" role="presentation"></a>تلاش مجدد هر یک چهارم ثانیه و توقف در صورت نیامدن پاسخ پس از گذشت یک ثانیه، قطعاً دلخواه است. البته حتی ممکن است که درخواست دریافت شود اما تابع گرداننده کند عمل کند که باعث شود عمل دریافت چندین بار صورت گیرد. ما توابع گرداننده را طوری می نویسیم که این مشکل را پوشش دهیم و پیغامهای تکراری ضرری برای سیستم نداشته باشند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_29" href="#p_2jmj7l5rSw_29" tabindex="-1" role="presentation"></a>طبیعتا قرار نیست که یک شبکهی بی نقص در سطح جهانی را امروز بسازیم. اما قابل قبول خواهد بود- کلاغها انتظارات خیلی بالایی در رابطه با محاسبات ندارند.</p>
<p><a class="p_ident" id="p_vgWUGmZvWm" href="#p_vgWUGmZvWm" tabindex="-1" role="presentation"></a>برای اینکه خودمان را به طور کامل از callback ها رها کنیم، پیشتر خواهیم رفت و همچنین یک پوشش برای تابع <code>defineRequestType</code> تعریف خواهیم کرد که به تابع گرداننده اجازه بدهد تا یک promise یا مقداری ساده را برگرداند و آن را به callback برای ما متصل کند.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_HejJcHo3dj" href="#c_HejJcHo3dj" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">requestType</span>(<span class="cm-def">name</span>, <span class="cm-def">handler</span>) {
<span class="cm-variable">defineRequestType</span>(<span class="cm-variable-2">name</span>, (<span class="cm-variable">nest</span>, <span class="cm-variable">content</span>, <span class="cm-variable">source</span>,
<span class="cm-variable">callback</span>) <span class="cm-operator">=></span> {
<span class="cm-keyword">try</span> {
<span class="cm-variable">Promise</span>.<span class="cm-property">resolve</span>(<span class="cm-variable-2">handler</span>(<span class="cm-variable">nest</span>, <span class="cm-variable">content</span>, <span class="cm-variable">source</span>))
.<span class="cm-property">then</span>(<span class="cm-def">response</span> <span class="cm-operator">=></span> <span class="cm-variable">callback</span>(<span class="cm-atom">null</span>, <span class="cm-variable-2">response</span>),
<span class="cm-def">failure</span> <span class="cm-operator">=></span> <span class="cm-variable">callback</span>(<span class="cm-variable-2">failure</span>));
} <span class="cm-keyword">catch</span> (<span class="cm-def">exception</span>) {
<span class="cm-variable">callback</span>(<span class="cm-variable-2">exception</span>);
}
});
}</pre>
<p><a class="p_ident" id="p_jC/YRC/GWd_3" href="#p_jC/YRC/GWd_3" tabindex="-1" role="presentation"></a><code><bdo></code><code>Promise.resolve</code><code></bdo></code> برای تبدیل مقدار بازگشتی از <code>handler</code> به یک promise استفاده میشود؛ اگر قبلا انجام نشده باشد.</p>
<p><a class="p_ident" id="p_JHm/3YHwce" href="#p_JHm/3YHwce" tabindex="-1" role="presentation"></a>توجه داشته باشید فراخوانی به تابع <code>handler</code> باید درون یک بلاک <code>try</code> قرار می گرفت، تا اطمینان حاصل شود هر استثنایی که تولید میکند مستقیما به تابع callback داده می شود. این به خوبی سختی رسیدگی درست به خطاها در مدل callback های خام را نشان می دهد – به راحتی ممکن است مدیریت صحیح استثناها را فراموش کنیم؛ مانند بالا و اگر این کار را انجام ندهید، شکستها به callback درستی گزارش نمیشوند. در promise ها، این کار را به طور خودکار انجام میشود بنابراین کمتر خطاساز خواهند بود.</p>
<h2><a class="h_ident" id="h_CLoO0aJnUK" href="#h_CLoO0aJnUK" tabindex="-1" role="presentation"></a>مجموعهای از promise ها</h2>
<p><a class="p_ident" id="p_2jmj7l5rSw_30" href="#p_2jmj7l5rSw_30" tabindex="-1" role="presentation"></a>هر کامپیوتر لانه دارای آرایهای از دیگر لانهها میباشد که درون محدودهی مخابره قرار دارند و آن را در خاصیت <code>neighbors</code> آن ذخیره میکند. برای بررسی اینکه کدام یک از آن ها در حال حاضر در دسترس هستند، میتوانید تابعی بنویسید که تلاش کند تا یک درخواست <code>“ping”</code> (درخواستی که فقط برای دریافت پاسخ ارسال میشود) را به هر یک از لانه ها ارسال کند و مشاهده کنید کدام درخواست پاسخ داده میشود.</p>
<p><a class="p_ident" id="p_ZoxCR+xi/G" href="#p_ZoxCR+xi/G" tabindex="-1" role="presentation"></a>زمانی که با مجموعهای از promise ها کار میکنید که در یک زمان یکسان اجرا میشوند، تابع <code><bdo></code><code>Promise.all</code><code></bdo></code> میتواند مفید باشد. این تابع یک promise را برمی گرداند که برای همهی promise های درون آرایه صبر میکند تا به نتیجه برسند و بعد نتیجه را درون یک آرایه از مقدارهایی که این promise ها تولید کرده اند می ریزد (به همان ترتیبی که در آرایهی اصلی آمده بودند). اگر یک promise رد شده باشد، نتیجهی <code><bdo></code><code>Promise.all</code><code></bdo></code> خودش نیز رد میشود.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_ZtEtR5AsHV" href="#c_ZtEtR5AsHV" tabindex="-1" role="presentation"></a><span class="cm-variable">requestType</span>(<span class="cm-string">"ping"</span>, () <span class="cm-operator">=></span> <span class="cm-string">"pong"</span>);
<span class="cm-keyword">function</span> <span class="cm-def">availableNeighbors</span>(<span class="cm-def">nest</span>) {
<span class="cm-keyword">let</span> <span class="cm-def">requests</span> <span class="cm-operator">=</span> <span class="cm-variable-2">nest</span>.<span class="cm-property">neighbors</span>.<span class="cm-property">map</span>(<span class="cm-def">neighbor</span> <span class="cm-operator">=></span> {
<span class="cm-keyword">return</span> <span class="cm-variable">request</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">neighbor</span>, <span class="cm-string">"ping"</span>)
.<span class="cm-property">then</span>(() <span class="cm-operator">=></span> <span class="cm-atom">true</span>, () <span class="cm-operator">=></span> <span class="cm-atom">false</span>);
});
<span class="cm-keyword">return</span> <span class="cm-variable">Promise</span>.<span class="cm-property">all</span>(<span class="cm-variable-2">requests</span>).<span class="cm-property">then</span>(<span class="cm-def">result</span> <span class="cm-operator">=></span> {
<span class="cm-keyword">return</span> <span class="cm-variable-2">nest</span>.<span class="cm-property">neighbors</span>.<span class="cm-property">filter</span>((<span class="cm-def">_</span>, <span class="cm-def">i</span>) <span class="cm-operator">=></span> <span class="cm-variable-2">result</span>[<span class="cm-variable-2">i</span>]);
});
}</pre>
<p><a class="p_ident" id="p_FykHZel6vX" href="#p_FykHZel6vX" tabindex="-1" role="presentation"></a>زمانی که یک همسایه در دسترس نیست، دوست نداریم که تمامی promise ترکیب شده با شکست روبرو شود، در آن موقع ما هنوز چیزی نمی دانیم. بنابراین تابعی که بر روی مجموعهی همسایهها اعمال شده است تا هر یک از آن ها را به درخواستهای promise تبدیل کند، گردانندههایی الصاق میکند تا درخواست های موفق <code>true</code> را تولید کنند و رد شده ها <code>false</code> را برگردانند.</p>
<p><a class="p_ident" id="p_cTnz5bc7io" href="#p_cTnz5bc7io" tabindex="-1" role="presentation"></a>در گردانندهای که برای promise ترکیبی در نظر گرفته شده، <code>filter</code> کار حذف آن عناصر، عناصری که مقدار متناظرشان برابر با false باشد را از آرایهی <code>neighbors</code> انجام میدهد. این کار از این واقعیت بهره می برد که <code>filter</code> اندیس عنصر فعلی در آرایه را به عنوان آرگومان دومش به تابع فیلترش (مانند <code>map</code>، <code>some</code> یا دیگر توابع ردهبالای آرایهها که به صورت مشابه عمل می کنند) ارسال میکند.</p>
<h2><a class="h_ident" id="h_O1/k+50Gd4" href="#h_O1/k+50Gd4" tabindex="-1" role="presentation"></a>جریان سیلگونه در شبکه</h2>
<p><a class="p_ident" id="p_2jmj7l5rSw_31" href="#p_2jmj7l5rSw_31" tabindex="-1" role="presentation"></a>این واقعیت که لانهها فقط میتوانند با همسایههایشان ارتباط برقرار کنند در مفید بودن این شبکه مانع ایجاد میکند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_32" href="#p_2jmj7l5rSw_32" tabindex="-1" role="presentation"></a>برای رساندن اطلاعات به کل شبکه، یک راه حل این است که نوع درخواستی تنظیم شود که به صورت خودکار به دیگر همسایهها مخابره شود. این همسایه ها سپس آن اطلاعات را به همسایههایشان منتقل می کنند تا زمانی که کل شبکه پیام را گرفته باشد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_wHo3Wk05Ya" href="#c_wHo3Wk05Ya" tabindex="-1" role="presentation"></a><span class="cm-keyword">import</span> {<span class="cm-def">everywhere</span>} <span class="cm-keyword">from</span> <span class="cm-string">"./crow-tech"</span>;
<span class="cm-variable">everywhere</span>(<span class="cm-def">nest</span> <span class="cm-operator">=></span> {
<span class="cm-variable-2">nest</span>.<span class="cm-property">state</span>.<span class="cm-property">gossip</span> <span class="cm-operator">=</span> [];
});
<span class="cm-keyword">function</span> <span class="cm-def">sendGossip</span>(<span class="cm-def">nest</span>, <span class="cm-def">message</span>, <span class="cm-def">exceptFor</span> <span class="cm-operator">=</span> <span class="cm-atom">null</span>) {
<span class="cm-variable-2">nest</span>.<span class="cm-property">state</span>.<span class="cm-property">gossip</span>.<span class="cm-property">push</span>(<span class="cm-variable-2">message</span>);
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">neighbor</span> <span class="cm-keyword">of</span> <span class="cm-variable-2">nest</span>.<span class="cm-property">neighbors</span>) {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">neighbor</span> <span class="cm-operator">==</span> <span class="cm-variable-2">exceptFor</span>) <span class="cm-keyword">continue</span>;
<span class="cm-variable">request</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">neighbor</span>, <span class="cm-string">"gossip"</span>, <span class="cm-variable-2">message</span>);
}
}
<span class="cm-variable">requestType</span>(<span class="cm-string">"gossip"</span>, (<span class="cm-def">nest</span>, <span class="cm-def">message</span>, <span class="cm-def">source</span>) <span class="cm-operator">=></span> {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">nest</span>.<span class="cm-property">state</span>.<span class="cm-property">gossip</span>.<span class="cm-property">includes</span>(<span class="cm-variable-2">message</span>)) <span class="cm-keyword">return</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`${</span><span class="cm-variable-2">nest</span>.<span class="cm-property">name</span><span class="cm-string-2">}</span> <span class="cm-string-2">received gossip '${</span>
<span class="cm-variable-2">message</span><span class="cm-string-2">}</span><span class="cm-string-2">' from ${</span><span class="cm-variable-2">source</span><span class="cm-string-2">}</span><span class="cm-string-2">`</span>);
<span class="cm-variable">sendGossip</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">message</span>, <span class="cm-variable-2">source</span>);
});</pre>
<p><a class="p_ident" id="p_qkpfgSXyNB" href="#p_qkpfgSXyNB" tabindex="-1" role="presentation"></a>برای جلوگیری از ارسال مداوم یک پیام یکسان در شبکه، هر لانه آرایهای از رشتههایی که قبلا دیده شده اند را نگه داری میکند. برای تعریف این آرایه، از تابع <code>everywhere</code> استفاده می کنیم – که کد را روی هر لانه اجرا میکند – برای افزودن یک خاصیت به شیء state لانه، که جایی است که ما وضعیت محلی لانه را نگه داری خواهیم کرد.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_33" href="#p_2jmj7l5rSw_33" tabindex="-1" role="presentation"></a>زمانی که یک لانه یک پیام تکراری را دریافت کند، که احتمالش در جایی که هر لانه پیامها را ندید بازارسال میکند وجود دارد، از آن پیام صرف نظر میشود. اما زمانی که پیامی جدید را دریافت میکند، آن پیام را با هیجان به همهی لانهها به جز لانهی فرستندهی پیام، ارسال میکند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_34" href="#p_2jmj7l5rSw_34" tabindex="-1" role="presentation"></a>این کار درست مانند پخش شدن جوهر در آب، خبر جدید را در شبکه پخش میکند. حتی زمانی که بعضی از ارتباطات در دسترس نیستند، اگر مسیر جایگزینی به یک لانهی مشخص وجود داشته باشد، خبر از آن طریق به آن لانه خواهد رسید.</p>
<p><a class="p_ident" id="p_COKlrJswvV" href="#p_COKlrJswvV" tabindex="-1" role="presentation"></a>این سبک از ارتباطات شبکهای را سیلگونه (flooding) می گویند – مانند سیل تمام شبکه را با اطلاعات فرا میگیرد تا این که همهی گره ها را پوشش دهد.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_35" href="#p_2jmj7l5rSw_35" tabindex="-1" role="presentation"></a>با فراخوانی <code>sendGossip</code> میتوانیم جریان پیغام درون روستا را مشاهده کنیم.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_KWBmLZ/aO7" href="#c_KWBmLZ/aO7" tabindex="-1" role="presentation"></a><span class="cm-variable">sendGossip</span>(<span class="cm-variable">bigOak</span>, <span class="cm-string">"Kids with airgun in the park"</span>);</pre>
<h2><a class="h_ident" id="h_53a+eNWwKU" href="#h_53a+eNWwKU" tabindex="-1" role="presentation"></a>مسیردهی پیام</h2>
<p><a class="p_ident" id="p_2jmj7l5rSw_36" href="#p_2jmj7l5rSw_36" tabindex="-1" role="presentation"></a>اگر یک گره بخواهید با یک گرهی مشخص دیگر ارتباط برقرار کند، سبک سیلگونه زیاد بهینه عمل نخواهد کرد. مخصوصا زمانی که شبکه بزرگ باشد، که باعث میشود میزان زیادی مخابره اطلاعات بدون کاربرد صورت گیرد.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_37" href="#p_2jmj7l5rSw_37" tabindex="-1" role="presentation"></a>یک راه حل جایگزین این است که برای پیامها راهی در نظر گرفته شود تا از گرهای به گرههای دیگر بپرند تا به گرهی مقصد برسند. مشکل این روش این است که باید اطلاعاتی دربارهی نقشهی شبکه داشته باشیم. برای ارسال درخواستی به سمت یک گره دور، لازم است بدانیم کدام لانههای همسایه پیام را به مقصد نزدیک تر می کنند. ارسال آن به سمتی اشتباه ما را به هدف نمیرساند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_38" href="#p_2jmj7l5rSw_38" tabindex="-1" role="presentation"></a>به دلیل اینکه هر لانه فقط همسایههای مجاورش را میشناسد، اطلاعات کافی برای محاسبه یک مسیر را در دست ندارد. باید به شیوهای اطلاعات این اتصالات را بین همهی لانهها منتشر کنیم. ترجیحا به روشی که بتوان در آینده در آن تغییر ایجاد کرد مثلا زمانیکه یک لانه متروکه میشود یا لانهی جدیدی ساخته میشود.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_39" href="#p_2jmj7l5rSw_39" tabindex="-1" role="presentation"></a>میتوانیم دوباره به سراغ روش سیلگونه برویم، اما به جای استفاده از آن برای بررسی دریافت یک پیام، اکنون بررسی می کنیم آیا مجموعهی همسایهها برای یک گرهی مشخص با مجموعهای که اکنون برای آن در دسترس داریم مطابقت دارد یا خیر.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_68Z9trrpeS" href="#c_68Z9trrpeS" tabindex="-1" role="presentation"></a><span class="cm-variable">requestType</span>(<span class="cm-string">"connections"</span>, (<span class="cm-variable">nest</span>, {<span class="cm-property">name</span>, <span class="cm-property">neighbors</span>},
<span class="cm-variable">source</span>) <span class="cm-operator">=></span> {
<span class="cm-keyword">let</span> <span class="cm-def">connections</span> <span class="cm-operator">=</span> <span class="cm-variable">nest</span>.<span class="cm-property">state</span>.<span class="cm-property">connections</span>;
<span class="cm-keyword">if</span> (<span class="cm-variable">JSON</span>.<span class="cm-property">stringify</span>(<span class="cm-variable-2">connections</span>.<span class="cm-property">get</span>(<span class="cm-variable">name</span>)) <span class="cm-operator">==</span>
<span class="cm-variable">JSON</span>.<span class="cm-property">stringify</span>(<span class="cm-variable">neighbors</span>)) <span class="cm-keyword">return</span>;
<span class="cm-variable-2">connections</span>.<span class="cm-property">set</span>(<span class="cm-variable">name</span>, <span class="cm-variable">neighbors</span>);
<span class="cm-variable">broadcastConnections</span>(<span class="cm-variable">nest</span>, <span class="cm-variable">name</span>, <span class="cm-variable">source</span>);
});
<span class="cm-keyword">function</span> <span class="cm-def">broadcastConnections</span>(<span class="cm-def">nest</span>, <span class="cm-def">name</span>, <span class="cm-def">exceptFor</span> <span class="cm-operator">=</span> <span class="cm-atom">null</span>) {
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">neighbor</span> <span class="cm-keyword">of</span> <span class="cm-variable-2">nest</span>.<span class="cm-property">neighbors</span>) {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">neighbor</span> <span class="cm-operator">==</span> <span class="cm-variable-2">exceptFor</span>) <span class="cm-keyword">continue</span>;
<span class="cm-variable">request</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">neighbor</span>, <span class="cm-string">"connections"</span>, {
<span class="cm-property">name</span>,
<span class="cm-property">neighbors</span>: <span class="cm-variable-2">nest</span>.<span class="cm-property">state</span>.<span class="cm-property">connections</span>.<span class="cm-property">get</span>(<span class="cm-variable-2">name</span>)
});
}
}
<span class="cm-variable">everywhere</span>(<span class="cm-def">nest</span> <span class="cm-operator">=></span> {
<span class="cm-variable-2">nest</span>.<span class="cm-property">state</span>.<span class="cm-property">connections</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">Map</span>;
<span class="cm-variable-2">nest</span>.<span class="cm-property">state</span>.<span class="cm-property">connections</span>.<span class="cm-property">set</span>(<span class="cm-variable-2">nest</span>.<span class="cm-property">name</span>, <span class="cm-variable-2">nest</span>.<span class="cm-property">neighbors</span>);
<span class="cm-variable">broadcastConnections</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">nest</span>.<span class="cm-property">name</span>);
});</pre>
<p><a class="p_ident" id="p_mWQe772OAF" href="#p_mWQe772OAF" tabindex="-1" role="presentation"></a>در مقایسه از <code><bdo></code><code>JSON.stringify</code><code></bdo></code> استفاده میشود چرا که <code>==</code>، روی اشیاء و آرایهها، فقط زمانی true برمی گرداند که هر دو طرف دارای مقدار یکسانی باشند، که چیزی نیست که ما در اینجا لازم داریم. مقایسهی رشتههای JSON جالب به نظر نمیرسد اما روشی موثر برای مقایسهی محتوای آن ها است.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_40" href="#p_2jmj7l5rSw_40" tabindex="-1" role="presentation"></a>گرهها بلافاصله شروع به مخابرهی اتصالاتشان می کنند، که باید به سرعت به هر لانه یک نقشه از گراف فعلی شبکه را بدهد، مگر اینکه بعضی از لانهها کلا در دسترس نباشند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_41" href="#p_2jmj7l5rSw_41" tabindex="-1" role="presentation"></a>یکی از کارهایی که در گرافها میتوان انجام داد پیدا کردن مسیرها در آنها است ، همانطور که در <a href="07_robot.html">فصل 7</a> دیدیم. اگر مسیری به سمت یک مقصد پیام داشته باشیم، می دانیم از کدام جهت باید اقدام به ارسال آن کنیم.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_42" href="#p_2jmj7l5rSw_42" tabindex="-1" role="presentation"></a>این تابع <code>findRoute</code>، که بسیار شباهت به تابع <code>findRoute</code> <a href="07_robot.html#findRoute">فصل 7</a> دارد، برای رسیدن به یک گره مشخص شده در شبکه به جستجو می پردازد. اما به جای برگرداندن تمام مسیر، فقط گام بعدی را برمی گرداند. لانهی بعدی خودش، از اطلاعات فعلی اش در رابطه با شبکه استفاده خواهد کرد و تصمیم میگیرد که کجا پیغام را بفرستد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_BDriUx0MeG" href="#c_BDriUx0MeG" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">findRoute</span>(<span class="cm-def">from</span>, <span class="cm-def">to</span>, <span class="cm-def">connections</span>) {
<span class="cm-keyword">let</span> <span class="cm-def">work</span> <span class="cm-operator">=</span> [{<span class="cm-property">at</span>: <span class="cm-variable-2">from</span>, <span class="cm-property">via</span>: <span class="cm-atom">null</span>}];
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">i</span> <span class="cm-operator">=</span> <span class="cm-number">0</span>; <span class="cm-variable-2">i</span> <span class="cm-operator"><</span> <span class="cm-variable-2">work</span>.<span class="cm-property">length</span>; <span class="cm-variable-2">i</span><span class="cm-operator">++</span>) {
<span class="cm-keyword">let</span> {<span class="cm-def">at</span>, <span class="cm-def">via</span>} <span class="cm-operator">=</span> <span class="cm-variable-2">work</span>[<span class="cm-variable-2">i</span>];
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">next</span> <span class="cm-keyword">of</span> <span class="cm-variable-2">connections</span>.<span class="cm-property">get</span>(<span class="cm-variable-2">at</span>) <span class="cm-operator">||</span> []) {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">next</span> <span class="cm-operator">==</span> <span class="cm-variable-2">to</span>) <span class="cm-keyword">return</span> <span class="cm-variable-2">via</span>;
<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable-2">work</span>.<span class="cm-property">some</span>(<span class="cm-def">w</span> <span class="cm-operator">=></span> <span class="cm-variable-2">w</span>.<span class="cm-property">at</span> <span class="cm-operator">==</span> <span class="cm-variable-2">next</span>)) {
<span class="cm-variable-2">work</span>.<span class="cm-property">push</span>({<span class="cm-property">at</span>: <span class="cm-variable-2">next</span>, <span class="cm-property">via</span>: <span class="cm-variable-2">via</span> <span class="cm-operator">||</span> <span class="cm-variable-2">next</span>});
}
}
}
<span class="cm-keyword">return</span> <span class="cm-atom">null</span>;
}</pre>
<p><a class="p_ident" id="p_2jmj7l5rSw_43" href="#p_2jmj7l5rSw_43" tabindex="-1" role="presentation"></a>اکنون میتوانیم تابعی بسازیم که میتواند پیغامها را به نقاط دور ارسال کند. اگر پیام مورد نظر مقصدش یک همسایهی مجاور بود، به طور معمولی تحویل داده میشود. در غیر این صورت، درون یک شیء قرار گرفته و به همسایهای ارسال میشود که به هدف نزدیک تر است، با استفاده از نوع درخواست <code>"route"</code> که باعث میشود آن همسایه نیز این رفتار را تکرار کند.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_qgwq/4PpOq" href="#c_qgwq/4PpOq" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">routeRequest</span>(<span class="cm-def">nest</span>, <span class="cm-def">target</span>, <span class="cm-def">type</span>, <span class="cm-def">content</span>) {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">nest</span>.<span class="cm-property">neighbors</span>.<span class="cm-property">includes</span>(<span class="cm-variable-2">target</span>)) {
<span class="cm-keyword">return</span> <span class="cm-variable">request</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">target</span>, <span class="cm-variable-2">type</span>, <span class="cm-variable-2">content</span>);
} <span class="cm-keyword">else</span> {
<span class="cm-keyword">let</span> <span class="cm-def">via</span> <span class="cm-operator">=</span> <span class="cm-variable">findRoute</span>(<span class="cm-variable-2">nest</span>.<span class="cm-property">name</span>, <span class="cm-variable-2">target</span>,
<span class="cm-variable-2">nest</span>.<span class="cm-property">state</span>.<span class="cm-property">connections</span>);
<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable-2">via</span>) <span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">Error</span>(<span class="cm-string-2">`No route to ${</span><span class="cm-variable-2">target</span><span class="cm-string-2">}</span><span class="cm-string-2">`</span>);
<span class="cm-keyword">return</span> <span class="cm-variable">request</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">via</span>, <span class="cm-string">"route"</span>,
{<span class="cm-property">target</span>, <span class="cm-property">type</span>, <span class="cm-property">content</span>});
}
}
<span class="cm-variable">requestType</span>(<span class="cm-string">"route"</span>, (<span class="cm-def">nest</span>, {<span class="cm-def">target</span>, <span class="cm-def">type</span>, <span class="cm-def">content</span>}) <span class="cm-operator">=></span> {
<span class="cm-keyword">return</span> <span class="cm-variable">routeRequest</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">target</span>, <span class="cm-variable-2">type</span>, <span class="cm-variable-2">content</span>);
});</pre>
<p><a class="p_ident" id="p_2jmj7l5rSw_44" href="#p_2jmj7l5rSw_44" tabindex="-1" role="presentation"></a>اکنون میتوانیم پیامی به لانهای که در برج کلیسا قرار دارد ارسال کنیم که چهار گام در شبکه نیاز دارد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_S73i8jJBHA" href="#c_S73i8jJBHA" tabindex="-1" role="presentation"></a><span class="cm-variable">routeRequest</span>(<span class="cm-variable">bigOak</span>, <span class="cm-string">"Church Tower"</span>, <span class="cm-string">"note"</span>,
<span class="cm-string">"Incoming jackdaws!"</span>);</pre>
<p><a class="p_ident" id="p_2jmj7l5rSw_45" href="#p_2jmj7l5rSw_45" tabindex="-1" role="presentation"></a>تاکنون لایههای متعددی از قابلیتها را روی یک سیستم ارتباطی اولیه ساخته ایم تا استفاده از آن را راحت و سرراست کنیم. این مدل (البته ساده شدهی) خوبی از چگونگی عملکرد شبکههای کامپیوتر در واقعیت است.</p>
<p><a class="p_ident" id="p_1oBJu4VFTA" href="#p_1oBJu4VFTA" tabindex="-1" role="presentation"></a>یک خاصیت متمایز کننده در شبکههای کامپیوتری این است که آن ها قابل اتکا نیستند – تجریدهایی که بر اساس آنها انجام می شود میتوانند مفید باشند، اما شکست شبکه را نمیتوان با آنها پوشش داد. بنابراین برنامهنویسی تحت شبکه نوعا با انتظار خرابی (failure) در شبکه و مدیریت آن سر و کار دارد.</p>
<h2><a class="h_ident" id="h_qZQ84odw86" href="#h_qZQ84odw86" tabindex="-1" role="presentation"></a>توابع Async</h2>
<p><a class="p_ident" id="p_2jmj7l5rSw_46" href="#p_2jmj7l5rSw_46" tabindex="-1" role="presentation"></a>برای ذخیرهی اطلاعات مهم، کلاغها اطلاعات را بین لانهها تکثیر می کنند. در این روش ، زمانی که یک شاهین یکی از لانهها را از بین می برد، اطلاعات از بین نخواهند رفت.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_47" href="#p_2jmj7l5rSw_47" tabindex="-1" role="presentation"></a>برای بازیابی یک بخش از اطلاعات که در بافت موجود در خود لانه وجود ندارد، یک کامپیوتر لانه ممکن است با لانههای تصادفی در شبکه ارتباط بگیرد تا اینکه آن لانهای که اطلاعات را دارد پیدا شود.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_QLKv1tpg1i" href="#c_QLKv1tpg1i" tabindex="-1" role="presentation"></a><span class="cm-variable">requestType</span>(<span class="cm-string">"storage"</span>, (<span class="cm-def">nest</span>, <span class="cm-def">name</span>) <span class="cm-operator">=></span> <span class="cm-variable">storage</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">name</span>));
<span class="cm-keyword">function</span> <span class="cm-def">findInStorage</span>(<span class="cm-def">nest</span>, <span class="cm-def">name</span>) {
<span class="cm-keyword">return</span> <span class="cm-variable">storage</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">name</span>).<span class="cm-property">then</span>(<span class="cm-def">found</span> <span class="cm-operator">=></span> {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">found</span> <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) <span class="cm-keyword">return</span> <span class="cm-variable-2">found</span>;
<span class="cm-keyword">else</span> <span class="cm-keyword">return</span> <span class="cm-variable">findInRemoteStorage</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">name</span>);
});
}
<span class="cm-keyword">function</span> <span class="cm-def">network</span>(<span class="cm-def">nest</span>) {
<span class="cm-keyword">return</span> <span class="cm-variable">Array</span>.<span class="cm-property">from</span>(<span class="cm-variable-2">nest</span>.<span class="cm-property">state</span>.<span class="cm-property">connections</span>.<span class="cm-property">keys</span>());
}
<span class="cm-keyword">function</span> <span class="cm-def">findInRemoteStorage</span>(<span class="cm-def">nest</span>, <span class="cm-def">name</span>) {
<span class="cm-keyword">let</span> <span class="cm-def">sources</span> <span class="cm-operator">=</span> <span class="cm-variable">network</span>(<span class="cm-variable-2">nest</span>).<span class="cm-property">filter</span>(<span class="cm-def">n</span> <span class="cm-operator">=></span> <span class="cm-variable-2">n</span> <span class="cm-operator">!=</span> <span class="cm-variable-2">nest</span>.<span class="cm-property">name</span>);
<span class="cm-keyword">function</span> <span class="cm-def">next</span>() {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">sources</span>.<span class="cm-property">length</span> <span class="cm-operator">==</span> <span class="cm-number">0</span>) {
<span class="cm-keyword">return</span> <span class="cm-variable">Promise</span>.<span class="cm-property">reject</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Error</span>(<span class="cm-string">"Not found"</span>));
} <span class="cm-keyword">else</span> {
<span class="cm-keyword">let</span> <span class="cm-def">source</span> <span class="cm-operator">=</span> <span class="cm-variable-2">sources</span>[<span class="cm-variable">Math</span>.<span class="cm-property">floor</span>(<span class="cm-variable">Math</span>.<span class="cm-property">random</span>() <span class="cm-operator">*</span>
<span class="cm-variable-2">sources</span>.<span class="cm-property">length</span>)];
<span class="cm-variable-2">sources</span> <span class="cm-operator">=</span> <span class="cm-variable-2">sources</span>.<span class="cm-property">filter</span>(<span class="cm-def">n</span> <span class="cm-operator">=></span> <span class="cm-variable-2">n</span> <span class="cm-operator">!=</span> <span class="cm-variable-2">source</span>);
<span class="cm-keyword">return</span> <span class="cm-variable">routeRequest</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">source</span>, <span class="cm-string">"storage"</span>, <span class="cm-variable-2">name</span>)
.<span class="cm-property">then</span>(<span class="cm-def">value</span> <span class="cm-operator">=></span> <span class="cm-variable-2">value</span> <span class="cm-operator">!=</span> <span class="cm-atom">null</span> <span class="cm-operator">?</span> <span class="cm-variable-2">value</span> : <span class="cm-variable-2">next</span>(),
<span class="cm-variable-2">next</span>);
}
}
<span class="cm-keyword">return</span> <span class="cm-variable-2">next</span>();
}</pre>
<p><a class="p_ident" id="p_scJSNRTgW+" href="#p_scJSNRTgW+" tabindex="-1" role="presentation"></a>به دلیل اینکه <code>connections</code> از جنس <code>Map</code> است، <code><bdo></code><code>Object.keys</code><code></bdo></code> روی آن جواب نمی دهد. متد keys در این شیء هم وجود دارد اما یک تکرارکننده (iterator) را برمی گرداند نه یک آرایه. یک تکرارکننده (یا مقدار قابل تکرار) را میتوان به وسیلهی <code><bdo></code><code>Array.from</code><code></bdo></code> به آرایه تبدیل کرد.</p>
<p><a class="p_ident" id="p_ZEHr4qx6tt" href="#p_ZEHr4qx6tt" tabindex="-1" role="presentation"></a>حتی با وجود استفاده از promise ها این کد نسبتا شکل خوبی ندارد. عملیات متعدد ناهمگام با هم زنجیر شده اند به صورتی که اصلا خوانا و واضح نیست. دوباره نیاز به یک تابع بازگشتی داریم (<code>next</code>) تا بتوانیم حلقه (looping) را بین لانهها مدل سازی کنیم.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_48" href="#p_2jmj7l5rSw_48" tabindex="-1" role="presentation"></a>و این که کاری که این کد درواقع انجام می دهد کاملا خطی است – همیشه منتظر اتمام عمل قبلی پیش از شروع عمل بعدی می ماند. در یک مدل برنامهنویسی همگام ، سادهتر می توان این کارها را پیاده سازی کرد.</p>
<p><a class="p_ident" id="p_FykHZel6vX_1" href="#p_FykHZel6vX_1" tabindex="-1" role="presentation"></a>خبر خوب این است که جاوااسکریپت این امکان را فراهم کرده است که کدهای شبه-همگام بنویسید. یک تابع <code>async</code> تابعی است که به طور ضمنی یک promise را برمیگرداند و میتواند در بدنهاش ، به وسیلهی دستور <code>await</code> منتظر دیگر promiseها باشد به طوری که همگام به نظر برسد.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_49" href="#p_2jmj7l5rSw_49" tabindex="-1" role="presentation"></a>میتوانیم تابع <code>findInStorage</code> را به شکل زیر بازنویسی کنیم.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_eDKVOF1oo/" href="#c_eDKVOF1oo/" tabindex="-1" role="presentation"></a><span class="cm-keyword">async</span> <span class="cm-keyword">function</span> <span class="cm-def">findInStorage</span>(<span class="cm-def">nest</span>, <span class="cm-def">name</span>) {
<span class="cm-keyword">let</span> <span class="cm-def">local</span> <span class="cm-operator">=</span> <span class="cm-keyword">await</span> <span class="cm-variable">storage</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">name</span>);
<span class="cm-keyword">if</span> (<span class="cm-variable-2">local</span> <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) <span class="cm-keyword">return</span> <span class="cm-variable-2">local</span>;
<span class="cm-keyword">let</span> <span class="cm-def">sources</span> <span class="cm-operator">=</span> <span class="cm-variable">network</span>(<span class="cm-variable-2">nest</span>).<span class="cm-property">filter</span>(<span class="cm-def">n</span> <span class="cm-operator">=></span> <span class="cm-variable-2">n</span> <span class="cm-operator">!=</span> <span class="cm-variable-2">nest</span>.<span class="cm-property">name</span>);
<span class="cm-keyword">while</span> (<span class="cm-variable-2">sources</span>.<span class="cm-property">length</span> <span class="cm-operator">></span> <span class="cm-number">0</span>) {
<span class="cm-keyword">let</span> <span class="cm-def">source</span> <span class="cm-operator">=</span> <span class="cm-variable-2">sources</span>[<span class="cm-variable">Math</span>.<span class="cm-property">floor</span>(<span class="cm-variable">Math</span>.<span class="cm-property">random</span>() <span class="cm-operator">*</span>
<span class="cm-variable-2">sources</span>.<span class="cm-property">length</span>)];
<span class="cm-variable-2">sources</span> <span class="cm-operator">=</span> <span class="cm-variable-2">sources</span>.<span class="cm-property">filter</span>(<span class="cm-def">n</span> <span class="cm-operator">=></span> <span class="cm-variable-2">n</span> <span class="cm-operator">!=</span> <span class="cm-variable-2">source</span>);
<span class="cm-keyword">try</span> {
<span class="cm-keyword">let</span> <span class="cm-def">found</span> <span class="cm-operator">=</span> <span class="cm-keyword">await</span> <span class="cm-variable">routeRequest</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">source</span>, <span class="cm-string">"storage"</span>,
<span class="cm-variable-2">name</span>);
<span class="cm-keyword">if</span> (<span class="cm-variable-2">found</span> <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) <span class="cm-keyword">return</span> <span class="cm-variable-2">found</span>;
} <span class="cm-keyword">catch</span> (<span class="cm-def">_</span>) {}
}
<span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">Error</span>(<span class="cm-string">"Not found"</span>);
}</pre>
<p><a class="p_ident" id="p_WGJXcLYhs0" href="#p_WGJXcLYhs0" tabindex="-1" role="presentation"></a>یک تابع async را میتوان با واژهی <code>async</code> قبل از کلیدواژهی <code>function</code> مشخص کرد. متدها را نیز میتوان با نوشتن آن قبل از نام متد تبدیل به <code>async</code> کرد . زمانی که تابع یا متدی با این خصوصیت فراخوانی شود یک promise را تولید خواهد کرد. به محض این که بدنهی تابع چیزی را برگرداند، آن promise نتیجهیابی میشود. اگر استثنایی تولید کند، promise رد میشود.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_04MPwtOsw6" href="#c_04MPwtOsw6" tabindex="-1" role="presentation"></a><span class="cm-variable">findInStorage</span>(<span class="cm-variable">bigOak</span>, <span class="cm-string">"events on 2017-12-21"</span>)
.<span class="cm-property">then</span>(<span class="cm-variable">console</span>.<span class="cm-property">log</span>);</pre>
<p><a class="p_ident" id="p_jC/YRC/GWd_4" href="#p_jC/YRC/GWd_4" tabindex="-1" role="presentation"></a>درون یک تابع <code>async،</code> واژهی <code>await</code> را میتوان در ابتدای یک عبارت قرار داد تا تابع برای دریافت نتیجهی promise منتظر بماند و بعد از آن به ادامهی اجرای تابع بپردازد.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_50" href="#p_2jmj7l5rSw_50" tabindex="-1" role="presentation"></a>این گونه توابع دیگر مانند توابع معمولی جاوااسکریپت از ابتدا تا انتها در یک حرکت اجرا نمیشوند. بلکه ممکن است در هر نقطهای که یک <code>await</code> دارند ایست کنند و بعدا به ادامه مسیرشان بپردازند.</p>
<p><a class="p_ident" id="p_FykHZel6vX_2" href="#p_FykHZel6vX_2" tabindex="-1" role="presentation"></a>برای کدهای ناهمگام مهم، استفاده از این روش معمولا مناسب تر است از استفاده از promise ها. حتی اگر لازم است که کاری انجام بدهید که مناسب مدل همگام نیست، مثل اجرای چندین کار در یک زمان، به آسانی میتوان <code>await</code> را با استفاده مستقیم از promise ها ترکیب کرد.</p>
<h2><a class="h_ident" id="h_FTcnrJ/73W" href="#h_FTcnrJ/73W" tabindex="-1" role="presentation"></a>مولدها Generators</h2>
<p><a class="p_ident" id="p_PuKbCCrieL" href="#p_PuKbCCrieL" tabindex="-1" role="presentation"></a>این قابلیت در توابع که میتوانند متوقف شده و بعدا دوباره به مسیرشان ادامه بدهند فقط مخصوص به توابع <code>async</code> نیست. جاوااسکریپت قابلیتی به نام توابع <em>generator</em> (مولد) دارد. این توابع به طور مشابه عمل می کنند اما بدون promise ها.</p>
<p><a class="p_ident" id="p_qes9UFq0bh" href="#p_qes9UFq0bh" tabindex="-1" role="presentation"></a>زمانی که تابعی را با <code><bdo></code><code>function*</code><code></bdo></code> (یک ستاره بعد از کلیدواژهی function قرار می دهید)، تعریف میکنید، باعث میشود که آن تابع به یک مولد تبدیل شود. زمانی که یک تابع مولد فراخوانی میشود، یک تکرارکننده (iterator) را برمی گرداند که پیش تر در <a href="06_object.html">فصل 6</a> دیده ایم.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_B4ek89g871" href="#c_B4ek89g871" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span><span class="cm-keyword">*</span> <span class="cm-def">powers</span>(<span class="cm-def">n</span>) {
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">current</span> <span class="cm-operator">=</span> <span class="cm-variable-2">n</span>;; <span class="cm-variable-2">current</span> <span class="cm-operator">*=</span> <span class="cm-variable-2">n</span>) {
<span class="cm-keyword">yield</span> <span class="cm-variable-2">current</span>;
}
}
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">power</span> <span class="cm-keyword">of</span> <span class="cm-variable">powers</span>(<span class="cm-number">3</span>)) {
<span class="cm-keyword">if</span> (<span class="cm-variable">power</span> <span class="cm-operator">></span> <span class="cm-number">50</span>) <span class="cm-keyword">break</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">power</span>);
}
<span class="cm-comment">// → 3</span>
<span class="cm-comment">// → 9</span>
<span class="cm-comment">// → 27</span></pre>
<p><a class="p_ident" id="p_2jmj7l5rSw_51" href="#p_2jmj7l5rSw_51" tabindex="-1" role="presentation"></a>در ابتدا، وقتی که تابع <code>powers</code> را فراخوانی میکنید، تابع در ابتدای خودش ایست می کند. هر بار که <code>next</code> را روی تکرارکننده فراخوانی میکنید، تابع تا رسیدن به یک عبارت <code>yield</code> اجرا میشود، و دوباره متوقف شده و مقداری که به وسیلهی <code>yield</code> حاصل شده است به عنوان مقدار بعدی تولیدی توسط تکرارکننده در نظر گرفته میشود. زمانی که تابع به پایان میرسد (که در این مثال هرگز اتفاق نمی افتد) تکرارکننده نیز به پایان میرسد.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_52" href="#p_2jmj7l5rSw_52" tabindex="-1" role="presentation"></a>نوشتن تکرارکنندهها اغلب در هنگام استفاده از توابع مولد ساده تر میباشد. تکرارکنندهی مربوط به کلاس <code>Groupe</code> (مربوط به تمرین <a href="06_object.html#group_iterator">فصل 6</a>) را میتوان با این مولد بازنویسی کرد:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_C6OZWjI9EM" href="#c_C6OZWjI9EM" tabindex="-1" role="presentation"></a><span class="cm-variable">Group</span>.<span class="cm-property">prototype</span>[<span class="cm-variable">Symbol</span>.<span class="cm-property">iterator</span>] <span class="cm-operator">=</span> <span class="cm-keyword">function</span><span class="cm-keyword">*</span>() {
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">i</span> <span class="cm-operator">=</span> <span class="cm-number">0</span>; <span class="cm-variable-2">i</span> <span class="cm-operator"><</span> <span class="cm-keyword">this</span>.<span class="cm-property">members</span>.<span class="cm-property">length</span>; <span class="cm-variable-2">i</span><span class="cm-operator">++</span>) {
<span class="cm-keyword">yield</span> <span class="cm-keyword">this</span>.<span class="cm-property">members</span>[<span class="cm-variable-2">i</span>];
}
};</pre>
<p><a class="p_ident" id="p_8S4ej0Vfgj" href="#p_8S4ej0Vfgj" tabindex="-1" role="presentation"></a>دیگر نیازی نیست که یک شیء را ایجاد کرده تا وضعیت تکرار را نگه داری کنیم – مولدها این کار را به صورت خودکار با ذخیرهی وضعیت محلیشان با هر بار خواندن yield انجام میدهند.</p>
<p><a class="p_ident" id="p_K+GGmrPUSt" href="#p_K+GGmrPUSt" tabindex="-1" role="presentation"></a>عبارتهای <code>yield</code> فقط میتوانند مستقیما درون خود تابع مولد استفاده شوند نه درون تابعی که درون مولد تعریف میکنید. وضعیتی که یک مولد در هنگام اجرای yield ذخیره میکند ، فقط شامل محیط محلی آن و موقعیتی که در آنجا yield انجام شده میشود.</p>
<p><a class="p_ident" id="p_QRQ2o23GXY" href="#p_QRQ2o23GXY" tabindex="-1" role="presentation"></a>یک تابع <code>async</code> یک نوع خاص از یک مولد است. در هنگام فراخوانی یک promise تولید می کند که در هنگام پایان تابع به نتیجه میرسد و زمانی که یک استثنا تولید می کنند reject میشوند. هر وقت که این تابع یک promise را yield میکند (به عبارتی با <code>await</code> منتظر یک promise می ماند)، نتیجهی آن promise (مقدار یا استثنای تولید شده) نتیجهی عبارت <code>await</code> خواهد بود.</p>
<h2><a class="h_ident" id="h_LixJ9Ii6vp" href="#h_LixJ9Ii6vp" tabindex="-1" role="presentation"></a>حلقهی رخداد - event loop</h2>
<p><a class="p_ident" id="p_2jmj7l5rSw_53" href="#p_2jmj7l5rSw_53" tabindex="-1" role="presentation"></a>برنامههای ناهمگام به صورت بخش بخش اجرا میشوند. هر بخش ممکن است کارهایی را شروع کند و کدهایی را هم برنامه ریزی کند که در صورت پایان یا شکست آن کارها اجرا شوند. بین این بخش ها، برنامه بیکار می نشیند و منتظر کار بعدی خواهد ماند.</p>
<p><a class="p_ident" id="p_mYfa1fy9XO" href="#p_mYfa1fy9XO" tabindex="-1" role="presentation"></a>بنابراین callbackها به طور مستقیم توسط کدی که آن ها را زمانبندی کرده اند فراخوانی نمیشوند. اگر من تابع <code>setTimeout</code> را از درون یک تابع فراخوانی کنم، آن تابع زمانی برگردانده میشود که تابع callback فراخوانی می شود. و زمانی که تابع callback اجرا و برمیگردد، کنترل برنامه به تابعی که آن را زمانبندی کرده بود بر نخواهد گشت.</p>
<p><a class="p_ident" id="p_l29z7MW66K" href="#p_l29z7MW66K" tabindex="-1" role="presentation"></a>رفتار ناهمگام، در پشتهی فراخوانی تهی تابع خودش اتفاق میافتد. این یکی از دلایلی است که بدون استفاده از promiseها، مدیریت استثناها در کدهای ناهمگام مشکل است. به دلیل اینکه هر callback با یک پشتهی تقریبا خالی شروع میشود، گردانندههای <code>catch</code> شما در پشته در هنگام بروز یک استثنا در پشته نخواهند بود.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_UswfjtMHu4" href="#c_UswfjtMHu4" tabindex="-1" role="presentation"></a><span class="cm-keyword">try</span> {
<span class="cm-variable">setTimeout</span>(() <span class="cm-operator">=></span> {
<span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">Error</span>(<span class="cm-string">"Woosh"</span>);
}, <span class="cm-number">20</span>);
} <span class="cm-keyword">catch</span> (<span class="cm-def">_</span>) {
<span class="cm-comment">// This will not run</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Caught!"</span>);
}</pre>
<p><a class="p_ident" id="p_f1DBc3jezP" href="#p_f1DBc3jezP" tabindex="-1" role="presentation"></a>اهمیتی ندارد چقدر این رخدادها به هم نزدیک باشند- مانند timeoutها یا درخواستهای وارده – ، یک محیط جاوااسکریپت فقط یک برنامه را در یک لحظه اجرا میکند. میتوان این را به عنوان اجرای یک حلقهی بزرگ دور برنامه شما تصور کرد که به آن حلقهی رخداد (event loop) می گویند. وقتی کاری دیگر برای انجام نمانده باشد ، حلقه از کار می ایستد. اما با ورود رخدادها، آنها به یک صف اضافه میشوند و کدهایشان یکی بعد از دیگری اجرا میشوند. بدلیل اینکه هیچگاه دو کار در یک لحظه اجرا نمیشود، کدهای کند و زمانگیر ممکن است در رسیدگی به دیگر رخدادها تاخیر ایجاد کنند.</p>
<p><a class="p_ident" id="p_VtPJSQviYI" href="#p_VtPJSQviYI" tabindex="-1" role="presentation"></a>در این مثال یک timeout تنظیم میشود، اما اجرای آن به بعد از زمان اجرای در نظر گرفته شده به تاخیر میافتد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Xozg9CCvVZ" href="#c_Xozg9CCvVZ" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">start</span> <span class="cm-operator">=</span> <span class="cm-variable">Date</span>.<span class="cm-property">now</span>();
<span class="cm-variable">setTimeout</span>(() <span class="cm-operator">=></span> {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Timeout ran at"</span>, <span class="cm-variable">Date</span>.<span class="cm-property">now</span>() <span class="cm-operator">-</span> <span class="cm-variable">start</span>);
}, <span class="cm-number">20</span>);
<span class="cm-keyword">while</span> (<span class="cm-variable">Date</span>.<span class="cm-property">now</span>() <span class="cm-operator"><</span> <span class="cm-variable">start</span> <span class="cm-operator">+</span> <span class="cm-number">50</span>) {}
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Wasted time until"</span>, <span class="cm-variable">Date</span>.<span class="cm-property">now</span>() <span class="cm-operator">-</span> <span class="cm-variable">start</span>);
<span class="cm-comment">// → Wasted time until 50</span>
<span class="cm-comment">// → Timeout ran at 55</span></pre>
<p><a class="p_ident" id="p_6zLBnp0Ex1" href="#p_6zLBnp0Ex1" tabindex="-1" role="presentation"></a>promise ها همیشه به عنوان یک رخداد جدید، رد یا حل و فصل میشوند. حتی اگر یک promise از پیش به نتیجه رسیده باشد، انتظار برای آن باعث میشود که callback شما بعد از پایان اسکریپت کنونی اجرا شود، نه به صورت فوری.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_B4TYo0a2ol" href="#c_B4TYo0a2ol" tabindex="-1" role="presentation"></a><span class="cm-variable">Promise</span>.<span class="cm-property">resolve</span>(<span class="cm-string">"Done"</span>).<span class="cm-property">then</span>(<span class="cm-variable">console</span>.<span class="cm-property">log</span>);
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Me first!"</span>);
<span class="cm-comment">// → Me first!</span>
<span class="cm-comment">// → Done</span></pre>
<p><a class="p_ident" id="p_2jmj7l5rSw_54" href="#p_2jmj7l5rSw_54" tabindex="-1" role="presentation"></a>در فصلهای بعدی انواع مختلفی از رخدادها را مشاهده خواهیم کرد که روی حلقهی رخدادها اجرا میشوند.</p>
<h2><a class="h_ident" id="h_gTEP5a9/M7" href="#h_gTEP5a9/M7" tabindex="-1" role="presentation"></a>باگها در مدل برنامهنویسی ناهمگام</h2>
<p><a class="p_ident" id="p_2jmj7l5rSw_55" href="#p_2jmj7l5rSw_55" tabindex="-1" role="presentation"></a>زمانی که برنامهی شما به صورت همگام اجرا میشود، در یک اجرای واحد، هیچ تغییر وضعیتی به جز آن هایی که خود برنامه ایجاد میکند وجود ندارد. در برنامههای ناهمگام قضیه متفاوت است- ممکن است شامل وقفههایی در اجرایشان باشند که در این وقفهها دیگر کدها میتوانند اجرا شوند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_56" href="#p_2jmj7l5rSw_56" tabindex="-1" role="presentation"></a>اجازه بدهید تا به مثالی نگاه کنیم. یکی از سرگرمیهای کلاغهای ما این است که تعداد جوجههایی که از تخم بیرون می آیند در طول یک سال در روستا را بشمارند. لانهها این عدد را در بافتهای ذخیرهسازیشان حفظ می کنند. کد پیش رو تلاش میکند تا تمامی اعداد موجود در همهی لانهها را برای یک سال مشخص بشمارد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_IMqeevTorV" href="#c_IMqeevTorV" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">anyStorage</span>(<span class="cm-def">nest</span>, <span class="cm-def">source</span>, <span class="cm-def">name</span>) {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">source</span> <span class="cm-operator">==</span> <span class="cm-variable-2">nest</span>.<span class="cm-property">name</span>) <span class="cm-keyword">return</span> <span class="cm-variable">storage</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">name</span>);
<span class="cm-keyword">else</span> <span class="cm-keyword">return</span> <span class="cm-variable">routeRequest</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">source</span>, <span class="cm-string">"storage"</span>, <span class="cm-variable-2">name</span>);
}
<span class="cm-keyword">async</span> <span class="cm-keyword">function</span> <span class="cm-def">chicks</span>(<span class="cm-def">nest</span>, <span class="cm-def">year</span>) {
<span class="cm-keyword">let</span> <span class="cm-def">list</span> <span class="cm-operator">=</span> <span class="cm-string">""</span>;
<span class="cm-keyword">await</span> <span class="cm-variable">Promise</span>.<span class="cm-property">all</span>(<span class="cm-variable">network</span>(<span class="cm-variable-2">nest</span>).<span class="cm-property">map</span>(<span class="cm-keyword">async</span> <span class="cm-def">name</span> <span class="cm-operator">=></span> {
<span class="cm-variable-2">list</span> <span class="cm-operator">+=</span> <span class="cm-string-2">`${</span><span class="cm-variable-2">name</span><span class="cm-string-2">}</span><span class="cm-string-2">: ${</span>
<span class="cm-keyword">await</span> <span class="cm-variable">anyStorage</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">name</span>, <span class="cm-string-2">`chicks in ${</span><span class="cm-variable-2">year</span><span class="cm-string-2">}</span><span class="cm-string-2">`</span>)
<span class="cm-string-2">}</span><span class="cm-string-2">\n`</span>;
}));
<span class="cm-keyword">return</span> <span class="cm-variable-2">list</span>;
}</pre>
<p><a class="p_ident" id="p_e1B1QHaHbK" href="#p_e1B1QHaHbK" tabindex="-1" role="presentation"></a>قسمت <code><bdo></code><code>async name =></code><code></bdo></code> نشان می دهد که توابع پیکانی arrow functions را همچنین میتوان به صورت <code>async</code> با قرار دادن واژهی <code>async</code> در ابتدای آن ایجاد کرد.</p>
<p><a class="p_ident" id="p_jC/YRC/GWd_5" href="#p_jC/YRC/GWd_5" tabindex="-1" role="presentation"></a>کد ما در نگاه اول نادرست به نظر نمیرسد... تابع پیکانی <code>async</code> بر روی مجموعهی لانهها نگاشت میشود، آرایهای از promiseها تولید میشود و سپس از <code><bdo></code><code>Promise.all</code><code></bdo></code> برای انتظار برای همهی اینها قبل از بازگشتن از لیستی که میسازند استفاده میشود.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_57" href="#p_2jmj7l5rSw_57" tabindex="-1" role="presentation"></a>اما این کد مطمئنا مشکل دارد. خروجی آن همیشه لانهای است که کندترین پاسخ را داشته است.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_GWIpudJZ4o" href="#c_GWIpudJZ4o" tabindex="-1" role="presentation"></a><span class="cm-variable">chicks</span>(<span class="cm-variable">bigOak</span>, <span class="cm-number">2017</span>).<span class="cm-property">then</span>(<span class="cm-variable">console</span>.<span class="cm-property">log</span>);</pre>
<p><a class="p_ident" id="p_2jmj7l5rSw_58" href="#p_2jmj7l5rSw_58" tabindex="-1" role="presentation"></a>میتوانید علت این مشکل را بیابید؟</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_59" href="#p_2jmj7l5rSw_59" tabindex="-1" role="presentation"></a>مشکل در قسمت عملگر <code><bdo></code><code>+=</code><code></bdo></code> قرار دارد، که مقدار فعلی لیست را در زمانی که دستور شروع به اجرا میکند میگیرد و بعد از اینکه دستور <code>await</code> به پایان میرسد، متغیر <code>list</code> را معادل با آن مقدار به اضافه رشتهی افزوده شده قرار می دهد.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_60" href="#p_2jmj7l5rSw_60" tabindex="-1" role="presentation"></a>اما در این میان جایی که دستور شروع به اجرا میکند و زمانی که به اتمام میرسد یک وقفهی ناهمگام وجود دارد. عبارت <code>map</code> قبل از اینکه چیزی به لیست اضافه شود، اجرا می شود بنابراین هرکدام از عملگرهای <code><bdo></code><code>+=</code><code></bdo></code> با یک رشتهی خالی شروع می کنند و به پایان می رسند، زمانی که بازیابی مخزنش به اتمام برسد، متغیر <code>list</code> را برابر با یک لیست تک-خطی قرار می دهد — نتیجه افزودن خطش به رشتهی تهی.</p>
<p><a class="p_ident" id="p_jC/YRC/GWd_6" href="#p_jC/YRC/GWd_6" tabindex="-1" role="presentation"></a>بجای اینکه لیست را با تغییر یک متغیر بسازیم، با برگرداندن خطوط از promiseهای نگاشت شده و فراخوانی <code>join</code> روی نتیجهی <code><bdo></code><code>Promise.all</code><code></bdo></code>، میتوان به سادگی از این اشکال جلوگیری کرد. به طور معمول، محاسبهی مقدارهای جدید نسبت به تغییر مقادیر فعلی کمتر خطاساز هستند.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_gkD231Soao" href="#c_gkD231Soao" tabindex="-1" role="presentation"></a><span class="cm-keyword">async</span> <span class="cm-keyword">function</span> <span class="cm-def">chicks</span>(<span class="cm-def">nest</span>, <span class="cm-def">year</span>) {
<span class="cm-keyword">let</span> <span class="cm-def">lines</span> <span class="cm-operator">=</span> <span class="cm-variable">network</span>(<span class="cm-variable-2">nest</span>).<span class="cm-property">map</span>(<span class="cm-keyword">async</span> <span class="cm-def">name</span> <span class="cm-operator">=></span> {
<span class="cm-keyword">return</span> <span class="cm-variable-2">name</span> <span class="cm-operator">+</span> <span class="cm-string">": "</span> <span class="cm-operator">+</span>
<span class="cm-keyword">await</span> <span class="cm-variable">anyStorage</span>(<span class="cm-variable-2">nest</span>, <span class="cm-variable-2">name</span>, <span class="cm-string-2">`chicks in ${</span><span class="cm-variable-2">year</span><span class="cm-string-2">}</span><span class="cm-string-2">`</span>);
});
<span class="cm-keyword">return</span> (<span class="cm-keyword">await</span> <span class="cm-variable">Promise</span>.<span class="cm-property">all</span>(<span class="cm-variable-2">lines</span>)).<span class="cm-property">join</span>(<span class="cm-string">"\n"</span>);
}</pre>
<p><a class="p_ident" id="p_U+0BUkeym7" href="#p_U+0BUkeym7" tabindex="-1" role="presentation"></a>اشتباهاتی شبیه این خیلی ساده اتفاق می افتند مخصوصا زمانی که از <code>await</code> استفاده می کنیم، و باید حواستان به جایی که وقفهها در کدتان رخ می دهد باشد. یک مزیت برنامه نویسی ناهمگام (چه با استفاده از callbackها ، promise ها یا await) به صورت صریح در جاوااسکریپت، این است که پیدا کردن این وقفهها نسبتا ساده است.</p>
<h2><a class="h_ident" id="h_EzvDUHyjs2" href="#h_EzvDUHyjs2" tabindex="-1" role="presentation"></a>خلاصه</h2>
<p><a class="p_ident" id="p_jU9HKeneWd_4" href="#p_jU9HKeneWd_4" tabindex="-1" role="presentation"></a>برنامهنویسی ناهمگام این امکان را فراهم می سازد که بتوان برای کارهای اجرایی زمانگیر صبر کرد بدون اینکه برنامه در حین انجام این کارها متوقف شود. محیطهای جاوااسکریپت نوعا این سبک از برنامهنویسی را با استفاده از callback ها پیاده سازی می کنند، توابعی که بعد از پایان یافتن کارهای مورد نظر، فراخوانی میشوند. یک حلقهی رخداد، این توابع callback را زمانبندی میکند تا در زمان مناسب فراخوانی شوند، یکی پس از دیگری، تا اجرای آنها با تداخل روبرو نشود.</p>
<p><a class="p_ident" id="p_jC/YRC/GWd_7" href="#p_jC/YRC/GWd_7" tabindex="-1" role="presentation"></a>برنامهنویسی ناهمگام با استفاده از promise ها آسان تر میشود، اشیائی که نمایندهی کارهایی هستند که ممکن است در آینده تکمیل شوند، و توابع <code>async</code>، که به شما این امکان را میدهند تا یک برنامهی ناهمگام را به شکلی بنویسید که انگار همگام است.</p>
<h2><a class="h_ident" id="h_ggOFdVwDCk" href="#h_ggOFdVwDCk" tabindex="-1" role="presentation"></a>تمرینها</h2>
<h3><a class="i_ident" id="i_4zlaDf3FG4" href="#i_4zlaDf3FG4" tabindex="-1" role="presentation"></a>رهگیری چاقوی جراحی</h3>
<p><a class="p_ident" id="p_2jmj7l5rSw_61" href="#p_2jmj7l5rSw_61" tabindex="-1" role="presentation"></a>کلاغهای روستا یک چاقوی جراحی قدیمی دارند که گاهی اوقات از آن برای ماموریتهای خاص استفاده میکنند — فرض کنید، برای بریدن توری درها یا بسته ها. برای اینکه بتوان به سرعت آن را رهگیری کرد، هربار که چاقو به لانهی دیگری منتقل می شد، یک مدخل به مخزن هر دو لانه اضافه می شد، لانهای که آن را داشت و لانهای که آن را دریافت کرده است و این مدخل با نام <code>"scalpel"</code> و با مقداری برای محل جدیدش ذخیره میشود.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_62" href="#p_2jmj7l5rSw_62" tabindex="-1" role="presentation"></a>این به این معنا است که برای پیدا کردن چاقو باید به تاریخچهی نشانههای موجود در مخزن مراجعه کرد تا اینکه به لانهای برسید که به خودش ارجاع می دهد.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_63" href="#p_2jmj7l5rSw_63" tabindex="-1" role="presentation"></a>یک تابع <code>async</code> به نام <code>locateScalpel</code> ایجاد کنید که این کار را انجام می دهد که از لانهای که روی آن اجرا میشود شروع میکند. میتوانید از تابع <code>anyStorage</code> که پیش تر تعریف شده برای دسترسی به لانههای مورد نظر استفاده کنید. چاقو از مدت زمان مدیدی است که بین لانهها دست به دست میشود که میتوان نتیجه گرفت که هر لانه یک مدخل <code>"scalpel"</code> را در مخزنش دارد.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_64" href="#p_2jmj7l5rSw_64" tabindex="-1" role="presentation"></a>در گام بعدی، همین تابع را بدون استفاده از <code>async</code> و <code>await</code> بنویسید.</p>
<p><a class="p_ident" id="p_jC/YRC/GWd_8" href="#p_jC/YRC/GWd_8" tabindex="-1" role="presentation"></a>آیا شکستهای درخواستها به درستی به عنوان عدم پذیرش یک promise برگردانده شده، در هر دو نسخه نمایش داده میشوند؟ چگونه؟</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_YnFVXk9tRo" href="#c_YnFVXk9tRo" tabindex="-1" role="presentation"></a><span class="cm-keyword">async</span> <span class="cm-keyword">function</span> <span class="cm-def">locateScalpel</span>(<span class="cm-def">nest</span>) {
<span class="cm-comment">// Your code here.</span>
}
<span class="cm-keyword">function</span> <span class="cm-def">locateScalpel2</span>(<span class="cm-def">nest</span>) {
<span class="cm-comment">// Your code here.</span>
}
<span class="cm-variable">locateScalpel</span>(<span class="cm-variable">bigOak</span>).<span class="cm-property">then</span>(<span class="cm-variable">console</span>.<span class="cm-property">log</span>);
<span class="cm-comment">// → Butcher Shop</span></pre>
<div class="solution"><div class="solution-text">
<p><a class="p_ident" id="p_2jmj7l5rSw_65" href="#p_2jmj7l5rSw_65" tabindex="-1" role="presentation"></a>این کار را میتوان به وسیلهی یک حلقه که درون لانهها را میگردد صورت داد، اگر مقداری مطابق نام لانهی فعلی پیدا کند آن را برمی گرداند و درغیر این صورت به سراغ لانهی بعدی می رود. در تابع <code>async</code>، یک دستور <code>for</code> یا <code>while</code> میتواند استفاده شود.</p>
<p><a class="p_ident" id="p_CZ6CO6EnK8_2" href="#p_CZ6CO6EnK8_2" tabindex="-1" role="presentation"></a>برای انجام این کار در یک تابع ساده، باید حلقهی خودتان را به وسیلهی یک تابع بازگشتی بنویسید. آسان ترین روش انجام این کار این است که تابع یک promise را با فراخوانی <code>then</code> روی promiseای که مقدار ذخیرهشده را برمیگرداند بنویسید. بسته به اینکه آن مقدار با نام لانهی فعلی مطابقت داشته باشد یا خیر ، تابع گرداننده، یا آن مقدار را برمیگرداند یا یک promise دیگر با فراخوانی دوبارهی تابع برمیگرداند.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_66" href="#p_2jmj7l5rSw_66" tabindex="-1" role="presentation"></a>فراموش نکنید که حلقه را با یک بار فراخوانی تابع بازگشتی از درون تابع اصلی شروع کنید.</p>
<p><a class="p_ident" id="p_FykHZel6vX_3" href="#p_FykHZel6vX_3" tabindex="-1" role="presentation"></a>در تابع <code>async</code>، promiseهای رد شده به وسیلهی <code>await</code> به استثنا تبدیل میشوند. زمانی که یک تابع <code>async</code> یک استثنا تولید میکند، promise آن رد شده است.</p>
<p><a class="p_ident" id="p_FykHZel6vX_4" href="#p_FykHZel6vX_4" tabindex="-1" role="presentation"></a>اگر تابع را بدون استفاده از <code>async</code> همانطور که مشخص شده است پیاده سازی کنید، نحوهی عملکرد پیشفرض <code>then</code> نیز باعث تولید یک شکست برای پایان دادن promise برگشتی میشود. اگر درخواستی با شکست روبرو شود، گردانندهای که به <code>then</code> ارسال میشود، فراخوانی نمیگردد و promiseای که برمیگرداند به همان دلیل رد میشود.</p>
</div></div>
<h3><a class="i_ident" id="i_Uy3j2HjqAG" href="#i_Uy3j2HjqAG" tabindex="-1" role="presentation"></a>ساختن Promise.all</h3>
<p><a class="p_ident" id="p_elCjOOWYcy" href="#p_elCjOOWYcy" tabindex="-1" role="presentation"></a>با داشتن یک آرایه از promise ها، متد <code><bdo></code><code>Promise.all</code><code></bdo></code> یک promise را برمی گرداند که برای همهی promise های موجود در آرایه، منتظر می ماند تا پایان یابند. در صورت موفقیت، آرایهای از مقدارهای نتایج تولید میشود. اگر یک promise موجود در آرایه با شکست روبرو شود، promise ای که به وسیله <code>all</code> برگردانده میشود نیز با شکست روبرو میشود، همراه با دلیل شکست promise مشکل خورده.</p>
<p><a class="p_ident" id="p_2jmj7l5rSw_67" href="#p_2jmj7l5rSw_67" tabindex="-1" role="presentation"></a>تابعی به نام <code>Promise_all</code> بنویسید که همین کار را انجام دهد.</p>
<p><a class="p_ident" id="p_FykHZel6vX_5" href="#p_FykHZel6vX_5" tabindex="-1" role="presentation"></a>به خاطر داشتهباشید که بعد از اینکه یک promise موفق شود یا با شکست روبرو شود، دیگر نمیتواند دوباره موفق یا شکست بخورد و فراخوانیهای بعدی به توابعی که برای نتیجهیابی آن اقدام می کنند صرف نظر میشوند. این میتواند راهی که شما شکستها را در promise تان رسیدگی میکنید ساده تر سازد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_70Eq9i3rpH" href="#c_70Eq9i3rpH" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">Promise_all</span>(<span class="cm-def">promises</span>) {
<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">Promise</span>((<span class="cm-def">resolve</span>, <span class="cm-def">reject</span>) <span class="cm-operator">=></span> {
<span class="cm-comment">// Your code here.</span>
});
}
<span class="cm-comment">// Test code.</span>
<span class="cm-variable">Promise_all</span>([]).<span class="cm-property">then</span>(<span class="cm-def">array</span> <span class="cm-operator">=></span> {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"This should be []:"</span>, <span class="cm-variable-2">array</span>);
});
<span class="cm-keyword">function</span> <span class="cm-def">soon</span>(<span class="cm-def">val</span>) {
<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">Promise</span>(<span class="cm-def">resolve</span> <span class="cm-operator">=></span> {
<span class="cm-variable">setTimeout</span>(() <span class="cm-operator">=></span> <span class="cm-variable-2">resolve</span>(<span class="cm-variable-2">val</span>), <span class="cm-variable">Math</span>.<span class="cm-property">random</span>() <span class="cm-operator">*</span> <span class="cm-number">500</span>);
});
}
<span class="cm-variable">Promise_all</span>([<span class="cm-variable">soon</span>(<span class="cm-number">1</span>), <span class="cm-variable">soon</span>(<span class="cm-number">2</span>), <span class="cm-variable">soon</span>(<span class="cm-number">3</span>)]).<span class="cm-property">then</span>(<span class="cm-def">array</span> <span class="cm-operator">=></span> {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"This should be [1, 2, 3]:"</span>, <span class="cm-variable-2">array</span>);
});
<span class="cm-variable">Promise_all</span>([<span class="cm-variable">soon</span>(<span class="cm-number">1</span>), <span class="cm-variable">Promise</span>.<span class="cm-property">reject</span>(<span class="cm-string">"X"</span>), <span class="cm-variable">soon</span>(<span class="cm-number">3</span>)])
.<span class="cm-property">then</span>(<span class="cm-def">array</span> <span class="cm-operator">=></span> {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"We should not get here"</span>);
})
.<span class="cm-property">catch</span>(<span class="cm-def">error</span> <span class="cm-operator">=></span> {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">error</span> <span class="cm-operator">!=</span> <span class="cm-string">"X"</span>) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Unexpected failure:"</span>, <span class="cm-variable-2">error</span>);
}
});</pre>
<div class="solution"><div class="solution-text">
<p><a class="p_ident" id="p_ZoxCR+xi/G_1" href="#p_ZoxCR+xi/G_1" tabindex="-1" role="presentation"></a>تابعی که به سازندهی <code>Promise</code> داده میشود نیاز خواهد داشت که <code>then</code> را روی هر یک از promiseهای آرایه فراخوانی کند. زمانی که یکی از این promiseها موفق شود، دو چیز لازم است تا اتفاق بیفتد. مقدار نتیجه باید در آرایه نتیجه و در موقعیت صحیح ذخیره شود، و باید بررسی کنیم که اگر این promise آخرین promise در حال بررسی بود، promise خودمان را به پایان برسانیم.</p>
<p><a class="p_ident" id="p_uGhSwQNlY2" href="#p_uGhSwQNlY2" tabindex="-1" role="presentation"></a>عمل آخر را میتوان به وسیلهی یک شمارنده استفاده کنیم که مقدار اولیهاش از اندازهی آرایه شروع میشود و با هر بار موفقیت یک promise، 1 واحد کاهش مییابد. وقتی این شمارنده به عدد 0 رسید، کار تمام است. مطمئن شوید که خالی بودن آرایهی ورودی را نیز بررسی کرده باشید ( که در این صورت هیچ promiseی حل و فصل نخواهد شد).</p>
<p><a class="p_ident" id="p_CZ6CO6EnK8_3" href="#p_CZ6CO6EnK8_3" tabindex="-1" role="presentation"></a>مدیریت شکست نیاز به کمی تفکر دارد اما درنهایت کاری بسیار ساده است. کافی است تابع <code>reject</code> متعلق به promise پوشش دهنده را به هر یک از promiseهای موجود در آرایه به عنوان گردانندهی <code>catch</code> ارسال کنید یا به عنوان آرگومان دوم به <code>then</code> بفرستید درنتیجه شکست در یکی از آن دو منجر به رد شدن کل promise پوشش دهنده میشود.</p>
</div></div><nav><a href="10_modules.html" title="previous chapter">◀</a> <a href="index.html" title="cover">◆</a> <a href="12_language.html" title="next chapter">▶</a></nav>
</article>