<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; JavaScript</title><link>https://academy.hsoub.com/programming/javascript/page/9/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; JavaScript</description><language>ar</language><item><title>&#x646;&#x633;&#x62E; &#x627;&#x644;&#x643;&#x627;&#x626;&#x646;&#x627;&#x62A; &#x628;&#x627;&#x644;&#x645;&#x631;&#x62C;&#x639; (by reference) &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D9%86%D8%B3%D8%AE-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A8%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-by-reference-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1006/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_09/5f65a890a0237_Object-copying-references.png.1cbde71b9daefce189c152468e42e3c8.png" /></p>

<p>
	أحد الاختلافات الأساسية بين الكائنات (objects) وأنواع البيانات الأولية (primitives) هو تخزينها ونسخها "بالطريقة المرجعية" (by reference).
</p>

<p>
	قيم أنواع البيانات الأولية: هي سلاسل وأرقام وقيم منطقية - تُسند أو تنسخ "كقيمة كاملة".
</p>

<p>
	فمثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4085_7" style="">
<span class="pln">let message </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Hello!"</span><span class="pun">;</span><span class="pln">
let phrase </span><span class="pun">=</span><span class="pln"> message</span><span class="pun">;</span></pre>

<p>
	نتيجة لتنفيذ الشيفرة السابقة لدينا متغيرين مستقلين، كلّ واحد يُخزن السلسلة <code>"Hello!‎"</code>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="51184" href="https://academy.hsoub.com/uploads/monthly_2020_09/variable-copy-value.png.c1d2b7676cf0b9d9d81a4adbdb917999.png" rel=""><img alt="variable-copy-value.png" class="ipsImage ipsImage_thumbnailed" data-fileid="51184" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_09/variable-copy-value.thumb.png.ff5c8fde95dde274b9efaffcea9851f2.png" style="width: 500px; height: auto;"></a>
</p>

<p>
	أما الكائنات ليست كذلك.
</p>

<p>
	<strong>لا يخزن المتغيّر الكائن نفسه، وإنما "عنوانه في الذاكرة"، بمعنى آخر "مرجع" له.</strong>
</p>

<p>
	هذا هو وصف الكائن:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4085_9" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	 
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="51182" href="https://academy.hsoub.com/uploads/monthly_2020_09/variable-contains-reference.png.4e3efec83441b1c304428dcd23524492.png" rel=""><img alt="variable-contains-reference.png" class="ipsImage ipsImage_thumbnailed" data-fileid="51182" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_09/variable-contains-reference.thumb.png.b81731a5fe70080bd1ee64df66bf541b.png" style="width: 500px; height: auto;"></a>
</p>

<p>
	هنا يُخزن الكائن في مكان ما في الذاكرة. والمتغير <code>user</code> له "مرجع" له.
</p>

<p>
	<strong>عند نسخ متغير نوعه كائن - ينسخ المرجع، ولا يتم تكرار الكائن.</strong>
</p>

<p>
	فمثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4085_11" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pln"> </span><span class="pun">};</span><span class="pln">

let admin </span><span class="pun">=</span><span class="pln"> user</span><span class="pun">;</span><span class="pln"> </span><span class="com">// نسخ المرجع</span></pre>

<p>
	الآن لدينا متغيرين، كل واحد له إشارة مرجعية لنفس الكائن:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="51183" href="https://academy.hsoub.com/uploads/monthly_2020_09/variable-copy-reference.png.0ce310a9d353d45bacfad77e9da05815.png" rel=""><img alt="variable-copy-reference.png" class="ipsImage ipsImage_thumbnailed" data-fileid="51183" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_09/variable-copy-reference.thumb.png.e2d53f1b728b67f18528d9dda2eadfb3.png" style="width: 500px; height: auto;"></a>
</p>

<p>
	يمكننا استخدام أي متغيّر للوصول إلى الكائن وتعديل محتوياته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4085_13" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'John'</span><span class="pln"> </span><span class="pun">};</span><span class="pln">

let admin </span><span class="pun">=</span><span class="pln"> user</span><span class="pun">;</span><span class="pln">

admin</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Pete'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// غيرت القيمة من خلال المتغير ‫admIn المرجعي</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫'Pete' ظهر التغيير على المتغير "user" المرجعي</span></pre>

<p>
	يوضح المثال أعلاه أن هناك كائنًا واحدًا فقط. كما لو كان لدينا خزانة لها مفتاحان واستخدمنا أحدهما وهو (<code>admin</code>) للوصول إليها. بعد ذلك، إذا استخدمنا لاحقًا مفتاحًا آخر وهو (<code>user</code>)، فيمكننا رؤية التغييرات.
</p>

<h2>
	الموازنة بحسب المرجع
</h2>

<p>
	إن معاملات المساواة <code>==</code> والمساواة الصارمة <code>===</code> الخاصة بالكائنات تعمل بأسلوب واحد.
</p>

<p>
	<strong>الكائنين يكونان متساويان فقط إذا كانا يشيران لنفس الكائن.</strong>
</p>

<p>
	هنا يشير المتغيرين لنفس الكائن، وبالتالي فإنهما متساويان:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4085_15" style="">
<span class="pln">let a </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
let b </span><span class="pun">=</span><span class="pln"> a</span><span class="pun">;</span><span class="pln"> </span><span class="com">// نسخ المرجع</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> a </span><span class="pun">==</span><span class="pln"> b </span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫true, كِلا المتغيّرين يشيران إلى الكائن نفسه</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> a </span><span class="pun">===</span><span class="pln"> b </span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span></pre>

<p>
	وهنا نلاحظ أن الكائنان مستقلان ولذلك غير متساويين،علمًا أن كلاهما فارغ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4085_17" style="">
<span class="pln">let a </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
let b </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln"> </span><span class="com">// كائنان منفصلان</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> a </span><span class="pun">==</span><span class="pln"> b </span><span class="pun">);</span><span class="pln"> </span><span class="com">// false</span></pre>

<p>
	لإجراء المقارنات مثل <code>obj1&gt; obj2</code> أو المقارنة مع قيمة <code>obj == 5</code> البدائي، تحولّ الكائنات لعناصر أولية. سوف ندرس كيفية عمل تحويل الكائنات قريبًا، ولكن في الحقيقة، نادرًا ما تحدث مثل هذه المقارنات، عادةً نتيجة لخطأ في الشيفرة البرمجية.
</p>

<h2>
	الاستنساخ والدمج
</h2>

<p>
	يؤدي نسخ متغير يخزن كائن إلى إنشاء مرجع آخر لنفس الكائن.
</p>

<p>
	ولكن ماذا لو احتجنا إلى تكرار كائنٍ ما؟ إنشاء نسخة مستقلة تمامًا، أي استنساخه؟
</p>

<p>
	هذا ممكن أيضًا، ولكنه أكثر صعوبة نوعًا ما، لأنه لا توجد طريقة مضمنة في لغة جافاسكربت لتأدية ذلك. في الواقع، نادرًا ما تكون هناك حاجة لذلك. وإن نسخ المرجع جيد في معظم الأوقات.
</p>

<p>
	ولكن إذا كنا نريد ذلك حقًا، فنحن بحاجة لإنشاء كائن جديد وتكرار بُنية الكائن الذي نريد نسخه من خلال تكرار خصائصه ونسخها على مستوى قيم الأنواع البدائية.
</p>

<p>
	هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4085_19" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">,</span><span class="pln">
  age</span><span class="pun">:</span><span class="pln"> </span><span class="lit">30</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

let clone </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln"> </span><span class="com">// كائن فارغ جديد</span><span class="pln">

</span><span class="com">// ‫لننسخ كلّ الخاصيات الموجودة في user إليه</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let key in user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  clone</span><span class="pun">[</span><span class="pln">key</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> user</span><span class="pun">[</span><span class="pln">key</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// الآن الكائن ‫clone هو نسخة مستقلة تمامًا لديه نفس خاصيات الكائن user</span><span class="pln">
clone</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Pete"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// غيرنا البيانات بداخله</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> user</span><span class="pun">.</span><span class="pln">name </span><span class="pun">);</span><span class="pln"> </span><span class="com">// ما يزال الاسم ‫John في الكائن الأساسي</span></pre>

<p>
	كما يمكننا استخدام الطريقة <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign" rel="external nofollow">Object.assign</a> لذلك.
</p>

<p>
	وتكون صياغتها هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4085_21" style="">
<span class="typ">Object</span><span class="pun">.</span><span class="pln">assign</span><span class="pun">(</span><span class="pln">dest</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">src1</span><span class="pun">,</span><span class="pln"> src2</span><span class="pun">,</span><span class="pln"> src3</span><span class="pun">...])</span></pre>

<ul>
<li>
		الوسيط الأول <code>dest</code> هي الكائن المستهدف.
	</li>
	<li>
		الوسيط التالي <code>src1, ..., srcN</code> (يمكن أن تكون كثيرة بحسب الحاجة) هي كائنات مصدر.
	</li>
	<li>
		تُنسخ خصائص جميع الكائنات المصدر <code>src1، ...، srcN</code> إلى الهدف <code>dest</code>. بمعنى آخر، تنسخ خصائص جميع الوسطاء الّتي تبدأ من الثانية في الكائن الأول.
	</li>
	<li>
		يعيد الاستدعاء الكائن <code>dest</code>.
	</li>
</ul>
<p>
	فمثلًا، يمكننا استخدامه لدمج عدة كائنات في واحد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4085_23" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pln"> </span><span class="pun">};</span><span class="pln">

let permissions1 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> canView</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
let permissions2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> canEdit</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">};</span><span class="pln">

</span><span class="com">// نسخ جميع الخاصيات من ‫permissions1 و permissions2 إلى user</span><span class="pln">
</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">assign</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> permissions1</span><span class="pun">,</span><span class="pln"> permissions2</span><span class="pun">);</span><span class="pln">

</span><span class="com">// now user = { name: "John", canView: true, canEdit: true }</span></pre>

<p>
	إذا كان اسم الخاصية المنسوخة موجودًا بالفعل، فعندها سيُستبدل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4085_25" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pln"> </span><span class="pun">};</span><span class="pln">

</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">assign</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Pete"</span><span class="pln"> </span><span class="pun">});</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="com">// now user = { name: "Pete" }</span></pre>

<p>
	يمكننا أيضًا استخدام التابع <code>Object.assign</code> لاستبدال حلقة <code>for..in</code> للاستنساخ البسيط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4085_27" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">,</span><span class="pln">
  age</span><span class="pun">:</span><span class="pln"> </span><span class="lit">30</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

let clone </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">assign</span><span class="pun">({},</span><span class="pln"> user</span><span class="pun">);</span></pre>

<p>
	تُنسخ جميع خصائص الكائن <code>user</code> في الكائن الفارغ ويُعاد.
</p>

<h2>
	الاستنساخ متداخل
</h2>

<p>
	حتى الآن افترضنا أن جميع خصائص الكائن <code>user</code> تكون قيم أولية. لكن ماذا عن الخصائص يمكن أن تكون مراجع لكائنات أخرى؟ كيف سنتعامل معها؟
</p>

<p>
	هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4085_29" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">,</span><span class="pln">
  sizes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">182</span><span class="pun">,</span><span class="pln">
    width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">50</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> user</span><span class="pun">.</span><span class="pln">sizes</span><span class="pun">.</span><span class="pln">height </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 182</span></pre>

<p>
	الآن لا يكفي نسخ <code>clone.sizes = user.sizes</code>، لأن <code>user.sizes</code> كائن، سينسخه بالطريقة المرجعية. لذا فإن <code>clone</code> و<code>user</code> سيتشاركان نفس الأحجام:
</p>

<p>
	هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4085_31" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">,</span><span class="pln">
  sizes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">182</span><span class="pun">,</span><span class="pln">
    width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">50</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

let clone </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">assign</span><span class="pun">({},</span><span class="pln"> user</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> user</span><span class="pun">.</span><span class="pln">sizes </span><span class="pun">===</span><span class="pln"> clone</span><span class="pun">.</span><span class="pln">sizes </span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫true, نفس الكائن</span><span class="pln">

</span><span class="com">// إن ‫user و clone لديهما نفس الحجم </span><span class="pln">
user</span><span class="pun">.</span><span class="pln">sizes</span><span class="pun">.</span><span class="pln">width</span><span class="pun">++;</span><span class="pln">       </span><span class="com">// غير خاصية من مكان معين</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">clone</span><span class="pun">.</span><span class="pln">sizes</span><span class="pun">.</span><span class="pln">width</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫51, تفقد هل الخاصية تغيرت في المكان الآخر (بالتأكيد)</span></pre>

<p>
	لإصلاح ذلك، يجب علينا استخدام حلقة الاستنساخ والّتي تفحص كلّ قيمة من قيم <code>user[key]</code>‎، فإذا كانت كائنًا، فعندها تُكرر هيكلها أيضًا. وهذا ما يسمى <strong>الاستنساخ العميق</strong> (Deep Cloning).
</p>

<p>
	هناك خوارزمية قياسية للاستنساخ العميق تتعامل مع الحالة المذكورة أعلاه وحالات أكثر تعقيدًا، تسمى خوارزمية الاستنساخ المهيكل (<a href="https://html.spec.whatwg.org/multipage/structured-data.html#safe-passing-of-structured-data" rel="external nofollow">Structured cloning algorithm</a>).
</p>

<p>
	كما يمكننا أيضًا استخدام العودية لتنفيذها. أو ببساطة ليس علينا إعادة اختراع العجلة، إذ يمكننا أخذ تطبيق جاهز لهذا الأمر، مثل التابع ‎<a href="https://lodash.com/docs#cloneDeep" rel="external nofollow">_.cloneDeep(obj)</a>‎ الموجود في مكتبة <a href="https://lodash.com/" rel="external nofollow">Lodash</a>.
</p>

<h2>
	الخلاصة
</h2>

<p>
	تُسند الكائنات وتنسخ بالطريقة المرجعية. بمعنى آخر، لا يخزن المتغير "قيمة الكائن"، ولكن "المكان المرجعي" (العنوان في الذاكرة) للقيمة. لذا فإن نسخ مثل هذا المتغير أو تمريره كوسيط لدالة يُنسخ هذا المرجع وليس الكائن.
</p>

<p>
	تُنفذُ جميع العمليات من خلال المراجع المنسوخة (مثل إضافة / إزالة الخاصيات) على نفس الكائن الفردي.
</p>

<p>
	لعمل "نسخة حقيقية" (نسخة مستقلة)، يمكننا استخدام <code>Object.assign</code> لما يسمى "بالنسخة السطحية" (إذ تُنسخ الكائنات المُتداخلة بالطريقة المرجعية) أو يمكننا النسخ من خلال دالّة "الاستنساخ العميق"، مثل الدالّة ‎<a href="https://lodash.com/docs#cloneDeep" rel="external nofollow">_.cloneDeep(obj)</a>‎.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/object-copy" rel="external nofollow">Object copying, references</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">1006</guid><pubDate>Fri, 18 Sep 2020 18:00:00 +0000</pubDate></item><item><title>&#x639;&#x627;&#x645;&#x644; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x628;&#x62F;&#x627;&#x644; &#x627;&#x644;&#x644;&#x627;&#x63A;&#x64A; ?? &#x627;&#x644;&#x62C;&#x62F;&#x64A;&#x62F; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%B9%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%A8%D8%AF%D8%A7%D9%84-%D8%A7%D9%84%D9%84%D8%A7%D8%BA%D9%8A-%D8%A7%D9%84%D8%AC%D8%AF%D9%8A%D8%AF-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1005/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_09/Nullish-coalescing-operator.png.69c7061b768364e8b1d055bfe0019039.png" /></p>

<p>
	<strong>ملاحظة مهمة</strong>: هذه إضافة حديثة للغة، لذلك تحتاج بعض المتصفحات القديمة لترقيع هذا النقص لأن ما سنشرحه هو إضافة حديثة للغة.
</p>

<p>
	يوفر عامل الاستبدال اللاغي <code>??</code> (Nullish coalescing operator) صيغة قصيرة لاختيار أول متغير معرّف (defined) من القائمة.
</p>

<p>
	نتيجة <code>a ?? b</code> هو:
</p>

<ul>
<li>
		سيُعيد <code>a</code> إذا لم تكن فارغة <code>null</code> أو غير معرّفة <code>undefined</code>،
	</li>
	<li>
		وإلا سيُعيد <code>b</code>.
	</li>
</ul>
<p>
	إذًا <code>x = a ?? b</code> هي اختصار:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6296_7" style="">
<span class="pln">x </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">a </span><span class="pun">!==</span><span class="pln"> </span><span class="kwd">null</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> a </span><span class="pun">!==</span><span class="pln"> </span><span class="kwd">undefined</span><span class="pun">)</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> a </span><span class="pun">:</span><span class="pln"> b</span><span class="pun">;</span></pre>

<p>
	إليك مثال أطول لتوضيح الأمر.
</p>

<p>
	تخيل أن لدينا مستخدم، وهناك متغيرات <code>FirstName</code> و<code>lastName</code> أو <code>nickName</code> لاسمهم الأول واسم العائلة أو اللقب. وويمكن أن تكون جميعها غير معرفة (undefined)، إذا قرر المستخدم عدم إدخال أي قيمة.
</p>

<p>
	نرغب في عرض اسم المستخدم: في حال أدخل أحد هذه المتغيرات الثلاثة، أو إظهار الإسم "مجهول" إذا لم يعيّن أي شيئ.
</p>

<p>
	دعنا نستخدم العامل <code>؟؟</code> لتحديد أول متغيّر معرّف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6296_9" style="">
<span class="pln">let firstName </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
let lastName </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
let nickName </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Supercoder"</span><span class="pun">;</span><span class="pln">

</span><span class="com">// سيعرض أول قيمة غير فارغة أو غير معرفة</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">firstName </span><span class="pun">??</span><span class="pln"> lastName </span><span class="pun">??</span><span class="pln"> nickName </span><span class="pun">??</span><span class="pln"> </span><span class="str">"Anonymous"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Supercoder</span></pre>

<h2>
	استخدام العامل ||
</h2>

<p>
	يمكن استخدام العامل <code>||</code> بنفس طريقة استخدام <code>??</code>. في الواقع، أي يمكننا استعمال <code>||</code> مكان <code>؟؟</code> في الشيفرة أعلاه والحصول على نفس النتيجة، كما شرحناها في مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B9%D8%A7%D9%85%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%86%D8%B7%D9%82%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r778/" rel="">العاملات المنطقية</a> في لغة جافاسكربت.
</p>

<p>
	الفرق المهم هو:
</p>

<ul>
<li>
		إن <code>||</code> تُعيد القيمة <em>الحقيقية</em> الأولى.
	</li>
	<li>
		بينما تُعيد <code>??</code> أول قيمة <em>معرّفة</em>.
	</li>
</ul>
<p>
	هذا مهم جدًا عندما نرغب في التعامل مع القيم غير المعرفة أو الفارغة (null/undefined) بطريقة مختلفة عن القيمة <code>0</code>.
</p>

<p>
	فمثلًا، إليك هذا الحالة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6296_11" style="">
<span class="pln">height </span><span class="pun">=</span><span class="pln"> height </span><span class="pun">??</span><span class="pln"> </span><span class="lit">100</span><span class="pun">;</span></pre>

<p>
	تعيّن هذه الشيفرة البرمجية المتغير <code>height</code> بالقيمة <code>100</code> في حال كان غير معرّف.
</p>

<p>
	دعونا نوازنه بالعامل <code>||</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6296_14" style="">
<span class="pln">let height </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">height </span><span class="pun">||</span><span class="pln"> </span><span class="lit">100</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 100</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">height </span><span class="pun">??</span><span class="pln"> </span><span class="lit">100</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 0</span></pre>

<p>
	هنا، <code>height || 100</code> يعامل الارتفاع الصفري على أنه غير معرّف، تمامًا مثل القيمة الفارغة <code>null</code> أو غير المعرفة <code>undefined</code> أو أي قيمة خاطئة أخرى. إذًا ستكون النتيجة هي <code>100</code>.
</p>

<p>
	أما <code>height ?? 100</code> يُعيد <code>100</code> فقط إذا كان المتغيّر <code>height</code> فارغًا <code>null</code> أو غير معرّف <code>undefined</code>. لذلك ستعرضُ الشيفرة السابقة قيمة الارتفاع <code>0</code> كما هي.
</p>

<p>
	يعتمد السلوك الأفضل على حالة الاستخدام الّتي نواجهها. عندما يكون الارتفاع صفر تكون القيمة صالحة، فمن الأفضل أن يكون العامل <code>??</code>.
</p>

<h2>
	أولوية العامل ??
</h2>

<p>
	إن أولوية العامل <code>??</code> منخفضة نوعًا ما: <code>5</code> في جدول <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table" rel="external nofollow">MDN</a>.
</p>

<p>
	إذًا تقيّم <code>??</code> بعد معظم العمليات الأخرى، ولكن قبل <code>=</code> و <code>?</code>.
</p>

<p>
	إذا احتجنا إلى اختيار قيمة بالعامل <code>??</code> في تعبير معقد، ففكر في إضافة الأقواس، هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6296_16" style="">
<span class="pln">let height </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
let width </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">

</span><span class="com">// هام: استخدم الأقواس</span><span class="pln">
let area </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">height </span><span class="pun">??</span><span class="pln"> </span><span class="lit">100</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">width </span><span class="pun">??</span><span class="pln"> </span><span class="lit">50</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">area</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 5000</span></pre>

<p>
	وإلا إذا حذفنا الأقواس، فإن عملية الضرب <code>*</code> لها أسبقية أعلى عامل <code>??</code> وستُنفذ قبلها.
</p>

<p>
	سيكون ذلك مشابه لهذا المثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6296_18" style="">
<span class="com">// يمكن أن تكون غير صحيحة</span><span class="pln">
let area </span><span class="pun">=</span><span class="pln"> height </span><span class="pun">??</span><span class="pln"> </span><span class="pun">(</span><span class="lit">100</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> width</span><span class="pun">)</span><span class="pln"> </span><span class="pun">??</span><span class="pln"> </span><span class="lit">50</span><span class="pun">;</span></pre>

<p>
	هناك أيضًا قيود متعلقة باللغة.
</p>

<p>
	<strong>لأسباب تتعلق بالسلامة، يُحظر استخدام العامل <code>??</code> مع العاملات <code>&amp;&amp;</code> و <code>||</code>.</strong>
</p>

<p>
	لاحظ الخطأ في الصياغة الموجود في الشيفرة أدناه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6296_20" style="">
<span class="pln">let x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">??</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ في الصياغة</span></pre>

<p>
	بالتأكيد إن القيد قابل للنقاش، ولكنه أُضيف إلى المواصفات القياسية للغة بغرض تجنب الأخطاء البرمجية، إذ يبدأ الناس في التبديل من <code>||</code> إلى <code>??</code>.
</p>

<p>
	استخدم الأقواس الصريحة لتجنب الأمر، هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6296_22" style="">
<span class="pln">let x </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">??</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln"> </span><span class="com">// تعمل دون أخطاء</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">x</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 2</span></pre>

<h2>
	الخلاصة
</h2>

<ul>
<li>
		<p>
			يوفر عامل الاستبدال اللاغي <code>??</code> طريقة مختصرة لاختيار قيمة "معرّفة" من القائمة.
		</p>

		<p>
			يستخدم لتعيين القيم الافتراضية للمتغيرات:
		</p>

		<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6296_24" style="">
<span class="com">// أسند القيمة 100 إلى المتغير ‫height إذا كان هذا الأخير فارغًا أو غير معرًف</span><span class="pln">
height </span><span class="pun">=</span><span class="pln"> height </span><span class="pun">??</span><span class="pln"> </span><span class="lit">100</span><span class="pun">;</span></pre>

		<p>
			 
		</p>
	</li>
	<li>
		<p>
			عامل <code>??</code> له أولوية منخفضة جدًا، وأعلى قليلًا من العاملات <code>?</code> و<code>=</code>.
		</p>
	</li>
	<li>
		<p>
			يحظر استخدامه مع العاملات <code>||</code> أو <code>&amp;&amp;</code> بدون أقواس صريحة.
		</p>
	</li>
</ul>
<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/nullish-coalescing-operator" rel="external nofollow">Nullish coalescing operator '??'‎</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">1005</guid><pubDate>Mon, 14 Sep 2020 18:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x623;&#x639;&#x62F;&#x627;&#x62F; &#x627;&#x644;&#x643;&#x628;&#x64A;&#x631;&#x629; BigInt &#x627;&#x644;&#x62C;&#x62F;&#x64A;&#x62F;&#x629; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A3%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A7%D9%84%D9%83%D8%A8%D9%8A%D8%B1%D8%A9-bigint-%D8%A7%D9%84%D8%AC%D8%AF%D9%8A%D8%AF%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r932/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/86.jpg.cbbcdf52736a2737ea8678eafa3127da.jpg" /></p>

<p>
	<strong>تحذير: إضافة حديثة للغة</strong><br>
	هذه إضافة حديثة للغة. يمكنك العثور على الحالة الحالية للدعم من <a href="https://caniuse.com/#feat=bigint" rel="external nofollow">هنا</a>.
</p>

<p>
	الأعداد الكبيرة <code>BigInt</code> هو متغيّر عدديّ خاص، يوفر دعمًا للأعداد الصحيحة ذات الطول العشوائي.
</p>

<p>
	تُنشأ الأعداد الكبيرة من خلال إلحاق الحرف <code>n</code> بنهاية العدد العادي، أو من خلال استدعاء الدالّة <code>BigInt</code> والّتي بدورها ستُنشئ عدد كبير من السلاسل أو الأعداد العادية وما إلى ذلك.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7988_7" style="">
<span class="kwd">const</span><span class="pln"> bigint </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1234567890123456789012345678901234567890n</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> sameBigint </span><span class="pun">=</span><span class="pln"> </span><span class="typ">BigInt</span><span class="pun">(</span><span class="str">"1234567890123456789012345678901234567890"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> bigintFromNumber </span><span class="pun">=</span><span class="pln"> </span><span class="typ">BigInt</span><span class="pun">(</span><span class="lit">10</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫مشابه تمامًا للطريقة  10n</span></pre>

<h2>
	المعاملات الرياضية
</h2>

<p>
	عمومًا يمكننا استخدام العدد الكبير مثل العدد العادي، فمثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7988_9" style="">
<span class="pln">alert</span><span class="pun">(</span><span class="lit">1n</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2n</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 3</span><span class="pln">

alert</span><span class="pun">(</span><span class="lit">5n</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2n</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 2</span></pre>

<p>
	الرجاء ملاحظة أن القسمة <code>5/2</code> تُعيد نتيجة مُقرّبة للصفر، بدون الجزء العشري. جميع العمليات على الأعداد الكبيرة ستُعيد أعداد كبيرة.
</p>

<p>
	لا يمكننا جمع الأعداد الكبيرة مع الأعداد العادية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7988_11" style="">
<span class="pln">alert</span><span class="pun">(</span><span class="lit">1n</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫خطأ: لايمكننا جمع الأعداد الكبيرة مع الأعداد العادية</span></pre>

<p>
	يجب علينا تحويلها بطريقة واضحة إن لزم الأمر: باستخدام <code>BigInt()‎</code> أو <code>Number()‎</code>، هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7988_13" style="">
<span class="pln">let bigint </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1n</span><span class="pun">;</span><span class="pln">
let number </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">

</span><span class="com">// تحويل عدد عادي إلى عدد كبير</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">bigint </span><span class="pun">+</span><span class="pln"> </span><span class="typ">BigInt</span><span class="pun">(</span><span class="pln">number</span><span class="pun">));</span><span class="pln"> </span><span class="com">// 3</span><span class="pln">

</span><span class="com">// تحويل عدد كبير إلى عدد عادي</span><span class="pln">
alert</span><span class="pun">(</span><span class="typ">Number</span><span class="pun">(</span><span class="pln">bigint</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> number</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 3</span></pre>

<p>
	تكون عمليات التحويل صامتة دائمًا، ولا تخطئ أبدًا، ولكن إذا كانت الأعداد الكبيرة ذات حجم كبير جدًا بحيث لن تتناسب مع العدد العادي، فستُحذف البتات الإضافية من العدد الكبير، لذلك يجب أن نكون حذرين عند إجراء مثل هذه التحويلات.
</p>

<p>
	<strong>ملاحظة</strong>: معامل الجمع الأحادي لا يطبق على الأعداد الكبيرة
</p>

<p>
	المعامل <code>‎+value</code>: هو طريقة معروفة لتحويل المتغير <code>value</code> إلى رقم.
</p>

<p>
	لا تدعم الأعداد الكبيرة هذه الطريقة لتجنب الفوضى:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7988_15" style="">
<span class="pln">let bigint </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1n</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> </span><span class="pun">+</span><span class="pln">bigint </span><span class="pun">);</span><span class="pln"> </span><span class="com">// خطأ</span></pre>

<p>
	لذلك يجب أن نستخدم <code>Number()‎</code> لتحويل العدد الكبير إلى عدد عادي.
</p>

<h2>
	عمليات الموازنة
</h2>

<p>
	تعمل عمليات الموازنة، مثل: <code>&lt;</code> و<code>&gt;</code> مع الأعداد الكبيرة والعادية على حدٍ سواء:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7988_17" style="">
<span class="pln">alert</span><span class="pun">(</span><span class="pln"> </span><span class="lit">2n</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1n</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> </span><span class="lit">2n</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span></pre>

<p>
	لاحظ أنه نظرًا لأن الأعداد العادية والأعداد الكبيرة تنتميان لأنواع مختلفة، فيمكن أن تكون متساوية <code>==</code>، ولكن ليست متساوية تمامًا <code>===</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7988_19" style="">
<span class="pln">alert</span><span class="pun">(</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1n</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">===</span><span class="pln"> </span><span class="lit">1n</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// false</span></pre>

<h2>
	العمليات المنطقية
</h2>

<p>
	تتصرف الأعداد الكبيرة مثل الأعداد العادية عندما تكون fداخل الجملة الشرطية <code>if</code> أو أي عمليات منطقية الأخرى.
</p>

<p>
	فمثلًا، في الجملة الشرطية <code>if</code> أدناه، تكون قيمة <code>0n</code> خاطئة، والقيم الأخرى صحيحة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7988_21" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="lit">0n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// لن يُشغّل مطلقًا</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تعمل المعاملات المنطقية مثل: <code>||</code> و<code>&amp;&amp;</code> وغيرهما مع الأعداد الكبيرة بطريقة مشابهة للأعداد العادية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7988_23" style="">
<span class="pln">alert</span><span class="pun">(</span><span class="pln"> </span><span class="lit">1n</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1 (1n is considered truthy)</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> </span><span class="lit">0n</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 2 (0n is considered falsy)</span></pre>

<h2>
	ترقيع مشاكل نقص الدعم
</h2>

<p>
	تعد عملية ترقيع الدعم للأعداد الكبيرة صعبة بعض الشيء. والسبب هو أن العديد من معاملات جافاسكربت، مثل: <code>+</code> و <code>-</code> وما إلى ذلك تتصرف بطريقة مختلفة مع الأعداد الكبيرة بالموازنة مع الأعداد العادية.
</p>

<p>
	فمثلًا، تُعيد عملية القسمة على الأعداد الكبيرة دائمًا أعداد كبيرة (وتقربها إن لزم الأمر).
</p>

<p>
	لتنفيذ مثل هذه السلوك عن طريق ترقيع نقص الدعم في هذه الحالة سنحتاج لتحليل الشيفرة واستبدال جميع هذه المعاملات بالدوالّ المناسبة. لكن القيام بذلك أمر مُرهق وسيُكون على حساب انخفاض الأداء.
</p>

<p>
	بالإضافة إلى ذلك لا يوجد طريقة شائعة ومعتمد بين مجتمع المطورين لترقيع نقص الدعم.
</p>

<p>
	على الرغم من ذلك، هنالك بعض المحاولات الجيدة لحل هذه المشكلة مثل المكتبة <a href="https://github.com/GoogleChromeLabs/jsbi" rel="external nofollow">JSBI</a>.
</p>

<p>
	تزودنا هذه المكتبة بالأعداد الكبيرة من خلال توابعها الخاصة. ويمكننا استخدامها بدلًا من الأعداد الكبيرة الأصيلة -بدون أي طريقة لترقيع لنقص الدعم-:
</p>

<table>
<thead><tr>
<th>
				العملية في مكتبة JSBI
			</th>
			<th>
				العملية في الأعداد الكبيرة الأصيلة
			</th>
			<th>
				العملية
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>a = JSBI.BigInt(789)</code>‎
			</td>
			<td>
				<code>a = BigInt(789)</code>‎
			</td>
			<td>
				إنشاء عدد كبير من عدد عادي
			</td>
		</tr>
<tr>
<td>
				<code>c = JSBI.add(a, b)</code>‎
			</td>
			<td>
				<code>c = a + b</code>
			</td>
			<td>
				الجمع
			</td>
		</tr>
<tr>
<td>
				<code>c = JSBI.subtract(a, b)</code>‎
			</td>
			<td>
				<code>c = a - b</code>
			</td>
			<td>
				الطرح
			</td>
		</tr>
<tr>
<td>
				...
			</td>
			<td>
				...
			</td>
			<td>
				...
			</td>
		</tr>
</tbody>
</table>
<p>
	… ثم نستخدم لترقيع الدعم ملحقات إضافية (مثل الملحق الإضافي Babel) وذلك لتحويل استدعاءات المكتبة JSBI إلى الأعداد الكبيرة الأصيلة للمتصفحات التي تدعمها.
</p>

<p>
	بتعبير آخر، يقترح هذا النهج أن نكتب الشيفرة في JSBI بدلًا من الأعداد الكبيرة الأصيلة. وتتعامل مكتبة JSBI مع الأعداد العادية والأعداد الكبيرة داخليًا، إذ تحاكي طريقة الاستخدام الصحيحة باتباع المواصفات عن كثب، لذلك ستكون الشيفرة جاهزة للتعامل مع الأعداد الكبيرة.
</p>

<p>
	يمكننا استخدام شيفرة البرمجية للمكتبة JSBI على حالها للمحركات التي لا تدعم الأعداد الكبيرة والّتي تدعمها على حدٍ سواء - إذ ستُرقع نقص الدعم بتحويل الاستدعاءات إلى الأعداد الكبيرة الأصيلة.
</p>

<h2>
	المصادر
</h2>

<ul>
<li>
		<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt" rel="external nofollow">موقع مطوري موزيلا MDN</a>.
	</li>
	<li>
		<a href="https://tc39.es/ecma262/#sec-bigint-objects" rel="external nofollow">المواصفات القياسية للمتغيّر</a>.
	</li>
</ul>
<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/bigint" rel="external nofollow">BigInt</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
}

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
]]></description><guid isPermaLink="false">932</guid><pubDate>Wed, 24 Jun 2020 11:52:47 +0000</pubDate></item><item><title>&#x62A;&#x642;&#x646;&#x64A;&#x629; Currying &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D9%82%D9%86%D9%8A%D8%A9-currying-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r931/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/85.jpg.56989bc97de0ef2e12b39f7c6db8708f.jpg" /></p>

<p>
	مفهوم <a href="https://en.wikipedia.org/wiki/Currying" rel="external nofollow">Currying</a> هو تقنية متقدمة للعمل مع الدوالّ. يستخدم في العديد من اللغات البرمجية الأخرى من بينهم جافاسكربت.
</p>

<p>
	Currying عبارة عن طريقة لتحويل الدوالّ التي تقيم الدالّة ذات الاستدعاء-أكثر من وسيط- <code>f (a، b، c)‎</code> لتصبح قابلة للاستدعاء -بوسيط واحد- هكذا <code>f(a)(b)(c)‎</code>.
</p>

<p>
	تحول تقنية Currying الدالّة فقط ولا تستدعها.
</p>

<p>
	لنرى في البداية مثالًا، لفهم ما نتحدث عنه فهمًا أفضل، وبعدها ننتقل للتطبيقات العملية.
</p>

<p>
	سننشئ دالة مساعدة باسم <code>curry (f)‎</code> والتي ستُنفذّ تقنية Currying على الدالّة <code>f</code> التي تقبل وسيطين. بتعبير آخر، تحول الدالة <code>curry(f)‎</code> الدالّة <code>f(a, b)‎</code> ذات الوسيطين إلى دالة تعمل كوسيط واحد<code>f(a)(b)‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2611_7" style="">
<span class="kwd">function</span><span class="pln"> curry</span><span class="pun">(</span><span class="pln">f</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// ‫الدالة curry(f)‎ هي من ستُنفذّ تحويل currying </span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">a</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// طريقة الاستخدام</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let curriedSum </span><span class="pun">=</span><span class="pln"> curry</span><span class="pun">(</span><span class="pln">sum</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> curriedSum</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)(</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 3</span></pre>

<p>
	كما نرى، فإن التنفيذ بسيط: إنه مجرد مغلفين للوسطاء.
</p>

<ul>
<li>
		نتيجة <code>curry(func)‎</code> هي دالة مغلّفة <code>function(a)‎</code>.
	</li>
	<li>
		عندما تسمى هكذا <code>curriedSum(1)‎</code>، تُحفظ الوسطاء في البيئة اللغوية للجافاسكربت (وهي نوع مواصفات اللغة تستخدم لتعريف ارتباط المعرّفات بالمتغيّرات والدوالّ المحددة وذلك بناءً على بنية الترابط اللغوية في شيفرة ECMAScript)، وتعيد غلاف جديد <code>function(b)‎</code>.
	</li>
	<li>
		ثمّ يُسمى هذا المغلّف باسم <code>2</code> نسبةً لوسطائه، ويُمرّر الاستدعاء إلى الدالّة <code>sum(a,b)‎</code> الأصليّة.
	</li>
</ul>
<p>
	من الأمثلة المتقدمة باستخدام تقنية currying هو <a href="https://lodash.com/docs#curry" rel="external nofollow">_curry</a> من مكتبة Lodash، والتي تُعيد غِلافًا الّذي يسمح باستدعاء الدالّة طبيعيًا وجزئيًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2611_9" style="">
<span class="kwd">function</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let curriedSum </span><span class="pun">=</span><span class="pln"> _</span><span class="pun">.</span><span class="pln">curry</span><span class="pun">(</span><span class="pln">sum</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫استخدام ‎_.curry من مكتبة lodash</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> curriedSum</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫النتيجة: 3، لايزال بإمكاننا استدعاؤه طبيعيًا</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> curriedSum</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)(</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫النتيجة: 3، الاستدعاء الجزئي</span></pre>

<h2>
	لماذا نحتاج لتقنية currying؟
</h2>

<p>
	لابد لنا من مثالٍ واقعي لفهم فوائد هذه التقنية.
</p>

<p>
	مثلًا، ليكن لدينا دالّة التسجيل <code>log(date, importance, message)‎</code> والّتي ستُنسّق المعلومات وتعرضها. مثل هذه الدوالّ مفيدة جدًا في المشاريع الحقيقة مثل: إرسال السجلات عبر الشبكة، في مثالنا سنستخدم فقط <code>alert</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2611_11" style="">
<span class="kwd">function</span><span class="pln"> log</span><span class="pun">(</span><span class="pln">date</span><span class="pun">,</span><span class="pln"> importance</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`[</span><span class="pln">$</span><span class="pun">{</span><span class="pln">date</span><span class="pun">.</span><span class="pln">getHours</span><span class="pun">()}:</span><span class="pln">$</span><span class="pun">{</span><span class="pln">date</span><span class="pun">.</span><span class="pln">getMinutes</span><span class="pun">()}]</span><span class="pln"> </span><span class="pun">[</span><span class="pln">$</span><span class="pun">{</span><span class="pln">importance</span><span class="pun">}]</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">message</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنُنفذ تقنية currying عليها!
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2611_13" style="">
<span class="pln">log </span><span class="pun">=</span><span class="pln"> _</span><span class="pun">.</span><span class="pln">curry</span><span class="pun">(</span><span class="pln">log</span><span class="pun">);</span></pre>

<p>
	بعد ذلك ستعمل دالّة <code>log</code> وفق المطلوب:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2611_15" style="">
<span class="pln">log</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">(),</span><span class="pln"> </span><span class="str">"DEBUG"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"some debug"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// log(a, b, c)</span></pre>

<p>
	… ولكنها تعمل أيضًا بعد تحويلها بتقنية currying:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2611_17" style="">
<span class="pln">log</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">())(</span><span class="str">"DEBUG"</span><span class="pun">)(</span><span class="str">"some debug"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// log(a)(b)(c)</span></pre>

<p>
	الآن يمكننا بسهولة إنشاء دالّة مناسبة للسجلات الحالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2611_19" style="">
<span class="com">// ‫logNow سيكون دالة جزئية من log مع وسيط أول ثابت</span><span class="pln">
let logNow </span><span class="pun">=</span><span class="pln"> log</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">());</span><span class="pln">

</span><span class="com">// استخدامه</span><span class="pln">
logNow</span><span class="pun">(</span><span class="str">"INFO"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"message"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// [HH:mm] INFO message</span></pre>

<p>
	الآن <code>logNow</code> هو نفس الدالة <code>log</code> بوسيط أول ثابت، بمعنى آخر "دالّة مطبقة جزئيًا" أو "جزئية" للاختصار.
</p>

<p>
	يمكننا المضي قدمًا وإنشاء دالّة مناسبة لسجلات تصحيح الأخطاء الحالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2611_21" style="">
<span class="pln">let debugNow </span><span class="pun">=</span><span class="pln"> logNow</span><span class="pun">(</span><span class="str">"DEBUG"</span><span class="pun">);</span><span class="pln">

debugNow</span><span class="pun">(</span><span class="str">"message"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// [HH:mm] DEBUG message</span></pre>

<p>
	إذا:
</p>

<ol>
<li>
		لم نفقد أي شيء بعد التحويل بتقنية currying: ولا يزال يمكننا أيضًا استدعاء الدالّة <code>log</code> طبيعيًا.
	</li>
	<li>
		يمكننا بسهولة إنشاء دوالّ جزئية مثل: سجلات اليوم.
	</li>
</ol>
<h2>
	الاستخدام المتقدم لتقنية currying
</h2>

<p>
	في حالة رغبتك في الدخول في التفاصيل، إليك طريقة الاستخدام المتقدمة لتقنية currying للدوال ذات الوسطاء المتعددة والّتي يمكننا استخدامها أعلاه.
</p>

<p>
	وهي مختصرة جدًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2611_23" style="">
<span class="kwd">function</span><span class="pln"> curry</span><span class="pun">(</span><span class="pln">func</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> curried</span><span class="pun">(...</span><span class="pln">args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">args</span><span class="pun">.</span><span class="pln">length </span><span class="pun">&gt;=</span><span class="pln"> func</span><span class="pun">.</span><span class="pln">length</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> func</span><span class="pun">.</span><span class="pln">apply</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(...</span><span class="pln">args2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> curried</span><span class="pun">.</span><span class="pln">apply</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">args2</span><span class="pun">));</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	إليك مثالًا لطريقة استخدامه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2611_25" style="">
<span class="kwd">function</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">,</span><span class="pln"> c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b </span><span class="pun">+</span><span class="pln"> c</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let curriedSum </span><span class="pun">=</span><span class="pln"> curry</span><span class="pun">(</span><span class="pln">sum</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> curriedSum</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 6, still callable normally</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> curriedSum</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)(</span><span class="lit">2</span><span class="pun">,</span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 6, currying of 1st arg</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> curriedSum</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)(</span><span class="lit">2</span><span class="pun">)(</span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 6, full currying</span></pre>

<p>
	تبدو تقنية currying للوهلة الأولى معقدةً، ولكنها في الحقيقة سهلة الفهم جدًا.
</p>

<p>
	نتيجة استدعاء <code>curry(func)‎</code> هي دالّة مُغلّفة <code>curried</code> والّتي تبدو هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2611_27" style="">
<span class="com">// func is the function to transform</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> curried</span><span class="pun">(...</span><span class="pln">args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">args</span><span class="pun">.</span><span class="pln">length </span><span class="pun">&gt;=</span><span class="pln"> func</span><span class="pun">.</span><span class="pln">length</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (1)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> func</span><span class="pun">.</span><span class="pln">apply</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> pass</span><span class="pun">(...</span><span class="pln">args2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (2)</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> curried</span><span class="pun">.</span><span class="pln">apply</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">args2</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	عند تشغيله، هناك فرعين للتنفيذ من الجملة الشرطية <code>if</code>:
</p>

<ol>
<li>
		سيكون الاستدعاء الآن هكذا: إن كان عدد الوسطاء <code>args</code> المُمرّرة هو نفس العدد الدالة الأصليّة المعرّفة لدينا (<code>func.length</code>) أو أكثر، عندها نمرّر الاستدعاء له فقط.
	</li>
	<li>
		وإلا سيكون الاستدعاء جزئيًا: لم تُستدعى الدالّة <code>func</code> بعد. وإنما أعيد بدلًا منها دالّة المغلِّفة أخرى <code>pass</code>، والتي ستُعيد تطبيق الدالة <code>curried</code> مع تقديم الوسطاء السابقين مع الوسطاء الجدد. وثمّ في استدعاء الجديد سنحصل إما على دالة جزئية جديدة (إن لم يكُ عدد الوسطاء كافي) أو النتيجة النهائية.
	</li>
</ol>
<p>
	لنرى مثلًا ما يحدث في حال الاستدعاء الدالة هكذا <code>sum(a, b, c)‎</code>. أي بثلاث وسطاء، وبذلك يكون <code>sum.length = 3</code>.
</p>

<p>
	عند استدعاء <code>curried(1)(2)(3)‎</code>:
</p>

<ol>
<li>
		الاستدعاء الأول <code>curried (1)‎</code> تحفظ <code>1</code> في بيئته اللغوية، ويُعيد دالّة المغلف <code>pass</code>.
	</li>
	<li>
		يُستدعى المغلّف <code>pass</code> مع الوسيط المُمرّر <code>(2)</code>: إذ يأخذ الوسطاء السابقين (<code>1</code>)، ويدمجهم مع الوسيط الذي حصل عليه وهو <code>(2)</code> ويستدعي الدالّة <code>curried(1, 2)‎</code> مع استخدام جميع ما حصل عليه من وسطاء. وبما أن عدد الوسطاء لا يزال أقل من 3 ، فإن الدالّة <code>curry</code> ستُعيد الدالّة <code>pass</code>.
	</li>
	<li>
		يُستدعى المغلّف <code>pass</code> مرة أخرى مع الوسيط المُمرّر <code>(3)</code>: ومن أجل الاستدعاء التالي <code>pass (3)‎</code> سيأخذ الوسطاء السابقين (<code>1</code>, <code>2</code>) ويضيف لهم الوسيط <code>3</code>، ليكون الاستدعاء هكذا <code>curried(1, 2, 3)‎</code>- أخيرًا لدينا ثلاث وسطاء، والّذين سيمرّروا للدالّة الأصلية. إذا لم تتوضح الفكرة حتى الآن ، فما عليك إلا تتبع تسلسل الاستدعاءات في عقلك أو على الورقة وستتوضح الأمور أكثر.
	</li>
</ol>
<p>
	<strong>ملاحظة</strong>: تعمل مع الدوالّ ثابتة الطول فقط
</p>

<p>
	يجب أن يكون للدالّة عدد ثابت من الوسطاء لتطبيق تقنية currying.
</p>

<p>
	إن استخدمت دالّةّ ما معاملات البقية، مثل: <code>f(...args)‎</code>، فلا يمكن معالجتها بهذه التقنية.
</p>

<p>
	<strong>ملاحظة</strong>: أكثر بقليل من مجرد تقنية تحويل
</p>

<p>
	انطلاقًا من التعريف، يجب على تقنية currying تحويل الدالّة <code>sum(a, b, c)‎</code> إلى <code>sum(a)(b)(c)‎</code>.
</p>

<p>
	لكن غالبية تطبيقات هذه التقنية في جافاسكربت متقدمة، وكما وضحنا سابقًا: فهي تحافظ على الدالّة قابلة للاستدعاء بعدة تنويعات للوسطاء المُمرّرة.
</p>

<h2>
	خلاصة
</h2>

<p>
	<em>تقنية Currying</em> هو عملية تحويل تجعل <code>f(a,b,c)‎</code> قابلة للاستدعاء كـ <code>f(a)(b)(c)‎</code>. عادةً ما تحافظ تطبيقات الجافاسكربت على الدوالّ بحيث تكون قابلة للاستدعاء بالشكل الطبيعي أو الجزئي إن كان عدد الوسطاء غير كافٍ.
</p>

<p>
	كما تسمح لنا هذه التقنية أيضًا بالحصول على دوالّ جزئية بسهولة. كما رأينا في مثال التسجيل، بعد تنفيذ هذه التقنية على الدالّة العالمية ذات الثلاث وسطاء <code>log(date, importance, message)‎</code> فإن ذلك سيمنحنا دوالّ جزئية عند استدعاؤها باستخدام وسيط واحد (هكذا <code>log(date)‎</code>) أو وسيطين (هكذا <code>log(date, importance)‎</code>).
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/currying-partials" rel="external nofollow">Currying</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">931</guid><pubDate>Wed, 24 Jun 2020 11:35:48 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x627;&#x644;&#x62F;&#x627;&#x644;&#x629; "Eval" &#x644;&#x62A;&#x646;&#x641;&#x64A;&#x630; &#x634;&#x64A;&#x641;&#x631;&#x629; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AF%D8%A7%D9%84%D8%A9-eval-%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r930/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/84.jpg.875a2d583fd693805fe9ca7719e62e40.jpg" /></p>

<p>
	تنفذ الدالّة <code>Eval</code> المضمّنة في اللغة الشيفرات البرمجية المُمرّرة لها كسلسلة نصية <code>string</code>.
</p>

<p>
	وصياغتها هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2973_7" style="">
<span class="pln">let result </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">eval</span><span class="pun">(</span><span class="pln">code</span><span class="pun">);</span></pre>

<p>
	فمثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2973_9" style="">
<span class="pln">let code </span><span class="pun">=</span><span class="pln"> </span><span class="str">'alert("Hello")'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">eval</span><span class="pun">(</span><span class="pln">code</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Hello</span></pre>

<p>
	يمكن أن تكون الشيفرة المُمررة للدالّة كبيرة وتحتوي على فواصل أسطر وتعريف دوالّ ومتغيّرات، وما إلى ذلك.
</p>

<p>
	ولكن نتيجة الدالّة <code>Eval</code> هي نتيجة أخر عبارة منفذة في الشيفرة.
</p>

<p>
	وإليك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2973_11" style="">
<span class="pln">let value </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">eval</span><span class="pun">(</span><span class="str">'1+1'</span><span class="pun">);</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 2</span></pre>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2973_13" style="">
<span class="pln">let value </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">eval</span><span class="pun">(</span><span class="str">'let i = 0; ++i'</span><span class="pun">);</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span></pre>

<p>
	تُنفذّ الشيفرة في البيئة الحالية للدالّة، ولذا فيمكنها رؤية المتغيرات الخارجية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2973_15" style="">
<span class="pln">let a </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let a </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">eval</span><span class="pun">(</span><span class="str">'alert(a)'</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 2</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

f</span><span class="pun">();</span></pre>

<p>
	كما يمكنها تعديل المتغيّرات الخارجية أيضًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2973_17" style="">
<span class="pln">let x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">eval</span><span class="pun">(</span><span class="str">"x = 10"</span><span class="pun">);</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">x</span><span class="pun">);</span><span class="pln"> </span><span class="com">// النتيجة: ‫10، تعدلت القيمة بنجاح</span></pre>

<p>
	في الوضع الصارم، تملك الدالّة <code>Eval</code> بيئة متغيّرات خاصة بها. لذا فلن تظهر الدوالّ والمتغيرات، المعرفة -داخل الدالة- للخارج وإنما ستبقى بداخلها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2973_19" style="">
<span class="com">// تذكر أن في الوضع الصارم يُشغّلُ تلقائيًا في الأمثلة الحيّة</span><span class="pln">
</span><span class="kwd">eval</span><span class="pun">(</span><span class="str">"let x = 5; function f() {}"</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="kwd">typeof</span><span class="pln"> x</span><span class="pun">);</span><span class="pln"> </span><span class="com">// undefined (المتحول غير مرئي هنا)</span><span class="pln">
</span><span class="com">// ‫الدالّة f غير مرئية هنا أيضًا</span></pre>

<p>
	بدون تفعيل "<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%B6%D8%B9-%D8%A7%D9%84%D8%B5%D8%A7%D8%B1%D9%85-%D8%A7%D9%84%D9%86%D9%85%D8%B7-%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%AB-%D9%84%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-r670/" rel="">الوضع صارم</a>"، لن يكون للدالّة <code>Eval</code> بيئة متغيرات خاصة بها، ولذلك سنرى المتغيّر <code>x</code> والدالّة <code>f</code> من خارج الدالّة.
</p>

<h2>
	استخدامات الدالة "Eval"
</h2>

<p>
	في طرق البرمجة الحديثة، نادرًا ما تستخدم الدالّة <code>Eval</code>. وغالبًا ما يقال عنها أنها أصل الشرور.
</p>

<p>
	والسبب بسيط: إذ كانت لغة جافاسكربت منذ زمن بعيد أضعف بكثير من الآن، ولم يكُ بالإمكان فعل إيّ شيء إلا باستخدام الدالّة <code>Eval</code>. ولكن ذلك الوقت مضى عليه عقد من الزمن.
</p>

<p>
	حاليًا، لا يوجد سبب وجيه لاستخدامها. ولو أن شخصًا يستخدمها الآن فلديه الإمكانية لاستبدالها بالبنية الحديثة للغة أو [بالوحدات]().
</p>

<p>
	لاحظ أن إمكانية وصول الدالة <code>eval</code> للمتغيرات الخارجية لها عواقب سيئة.
</p>

<p>
	إن عملية تصغير الشيفرة (هي الأدوات تستخدم لتصغير شيفرة الجافاسكربت قبل نشرها وذلك لتصغير حجمها أكثر من ذي قبل) تعيد تسمية المتغيّرات المحلية لأسماء أقصر (مثل <code>a</code> و<code>b</code> وما إلى ذلك) لتصغير الشيفرة. وعادةً ما تكون هذه العلمية آمنة، ولكن ليس في حال استخدام الدالّة <code>Eval</code>، إذ يمكننا الوصول للمتغيّرات المحلية من الشيفرة المُمررة للدالّة. لذا، لن تصغّر المتغيرات التي يحتمل أن تكون مرئية من الدالة <code>Eval</code>. مما سيُؤثر سلبًا على نسبة ضغط الشيفرة.
</p>

<p>
	يُعدّ استخدام المتغيّرات المحلية في الشيفرة بداخل الدالّة <code>Eval</code> من الممارسات البرمجية السيئة، لأنه يزيد صعوبة صيانة الشيفرة.
</p>

<p>
	هناك طريقتان لضمان الأمان الكامل عند مصادفتك مثل هذه المشاكل.
</p>

<p>
	<strong>إذا لم تستخدم الشيفرة الممررة للدالّة المتغيرات الخارجية، فمن الأفضل استدعاء الدالّة هكذا: <code>window.eval(...)‎</code></strong>
</p>

<p>
	بهذه الطريقة ستُنفذّ الشيفرة في النطاق العام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2973_21" style="">
<span class="pln">let x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
  let x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">
  window</span><span class="pun">.</span><span class="kwd">eval</span><span class="pun">(</span><span class="str">'alert(x)'</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1 (global variable)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<strong>إن احتاجت الشيفرة الممررة للدالة <code>Eval</code> لمتغيّرات خارجية، فغيّر <code>Eval</code> لتصبح <code>new Function</code> ومرّر المتغير كوسيط. هكذا:</strong>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2973_23" style="">
<span class="pln">let f </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Function</span><span class="pun">(</span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'alert(a)'</span><span class="pun">);</span><span class="pln">

f</span><span class="pun">(</span><span class="lit">5</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 5</span></pre>

<p>
	شرحنا في مقالٍ سابق تعلمنا كيفية استخدام [صياغة «الدالة الجديدة» new Function](). إذ باستخدام هذه الصياغة ستُنشأ دالة جديدة من السلسلة (String)، في النطاق العام. لذا لن تتمكن من رؤية المتغيرات المحلية. ولكن من الواضح أن تمريرها المتغيرات صراحة كوسطاء سيحلّ المشكلة، كما رأينا في المثال أعلاه.
</p>

<h2>
	خلاصة
</h2>

<p>
	سيُشغّل استدعاء الدالّة <code>eval(code)‎</code> الشيفرة البرمجية المُمرّرة ويعيد نتيجة العبارة الأخيرة.
</p>

<ul>
<li>
		نادرًا ما تستخدم هذه الدالّة في الإصدارات الحديثة للغة، إذ لا توجد حاجة ماسّة لها.
	</li>
	<li>
		يمكننا الوصول دائمًا للمتغيّرات الخارجية في الدالّة <code>eval</code>. ولكن يعدّ ذلك من الممارسات السيئة.
	</li>
	<li>
		بدلًا من ذلك يمكننا استخدام الدالة <code>eval</code> في النطاق العام، هكذا <code>window.eval(code)‎</code>.
	</li>
	<li>
		أو، إذا كانت الشيفرة الخاصة بك تحتاج لبعض البيانات من النطاق الخارجي، فاستخدم صياغة <code>الدالّة الجديدة</code> ومرّر لها المتغيرات كوسطاء.
	</li>
</ul>
<h2>
	التمارين
</h2>

<h3>
	آلة حاسبة باستخدام الدالة Eval
</h3>

<p>
	<em>الأهمية: 4</em>
</p>

<p>
	أنشئ آلة حاسبة تطالب بتعبير رياضي وتُعيد نتيجته.
</p>

<p>
	لا داعي للتحقق من صحة التعبير في هذا التمرين. فقط قيّم التعبير وأعد نتيجته.
</p>

<p>
	<a href="https://javascript.info/eval#" rel="external nofollow">لرؤية المثال الحي</a>
</p>

<h4>
	الحل
</h4>

<p>
	لنستخدم الدالة <code>eval</code> لحساب التعبير الرياضي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2973_25" style="">
<span class="pln">let expr </span><span class="pun">=</span><span class="pln"> prompt</span><span class="pun">(</span><span class="str">"Type an arithmetic expression?"</span><span class="pun">,</span><span class="pln"> </span><span class="str">'2*3+2'</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">eval</span><span class="pun">(</span><span class="pln">expr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	يستطيع المستخدم أيضًا إدخال أي نص أو شيفرة.
</p>

<p>
	لجعل الشيفرة آمنة، وحصرها للعمليات الرياضية فحسب، سنتحقق من <code>expr</code> باستخدام <a href="https://javascript.info/regular-expressions" rel="external nofollow">التعابير النمطية</a>، لكي لا تحتوي إلا على الأرقام والمعاملات رياضية.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/eval" rel="external nofollow">Eval: run a code string</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">930</guid><pubDate>Wed, 24 Jun 2020 11:28:39 +0000</pubDate></item><item><title>&#x627;&#x644;&#x648;&#x633;&#x64A;&#x637; Proxy &#x648;&#x627;&#x644;&#x645;&#x646;&#x639;&#x643;&#x633; Reflect &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%B3%D9%8A%D8%B7-proxy-%D9%88%D8%A7%D9%84%D9%85%D9%86%D8%B9%D9%83%D8%B3-reflect-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r929/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/83.jpg.a59d6c1ea15f2616d5cadc642739e5d0.jpg" /></p>

<p>
	يغلّف كائن الوكيل <code>Proxy</code> كائنًا آخر ويعترض عملياته مثل: خاصيات القراءة أو الكتابة وغيرهما. ويعالجها اختياريًا بمفرده، أو يسمح بشفافية للكائن التعامل معها بنفسه.
</p>

<p>
	تستخدم العديد من المكتبات وأُطر عمل المتصفح الوسطاء. وسنرى في هذا الفصل العديد من تطبيقاته العملية.
</p>

<h2>
	الوسيط Proxy
</h2>

<p>
	صياغته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_7" style="">
<span class="pln">let proxy </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> handler</span><span class="pun">)</span></pre>

<ul>
<li>
		<code>target</code> -- وهو الكائن الّذي سنغلِّفه يمكن أي يكون أي شيء بما في ذلك التوابع.
	</li>
	<li>
		<code>handler</code> -- لإعداد الوسيط: وهو كائن يحتوي على "الاعتراضات"، أي دوالّ اعتراض العمليات. مثل: الاعتراض <code>get</code> لاعتراض خاصية القراءة في الهدف <code>target</code>، الاعتراض <code>set</code> لاعتراض خاصية الكتابة في الهدف <code>target</code>، وهكذا.
	</li>
</ul>
<p>
	سيراقب الوسيط العمليات فإن كان لديه اعتراض مطابق في المعالج <code>handler</code> للعملية المنفذة عند الهدف، فعندها سينفذ الوسيط هذه العملية ويعالجها وإلا ستُنفذ هذه العملية من قِبل الهدف نفسه.
</p>

<p>
	لنأخذ مثالًا بسيطًا يوضح الأمر، لننشئ وسيطًا بدون أي اعتراضات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_9" style="">
<span class="pln">let target </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
let proxy </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{});</span><span class="pln"> </span><span class="com">// المعالج فارغ</span><span class="pln">

proxy</span><span class="pun">.</span><span class="pln">test </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln"> </span><span class="com">// ‫الكتابة على الوسيط (1)</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">target</span><span class="pun">.</span><span class="pln">test</span><span class="pun">);</span><span class="pln"> </span><span class="com">// النتيجة: ‫5، ظهرت الخاصية في كائن الهدف !</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">proxy</span><span class="pun">.</span><span class="pln">test</span><span class="pun">);</span><span class="pln"> </span><span class="com">// النتيجة: ‫5، يمكننا قرائتها من الوسيط أيضًا (2)</span><span class="pln">

</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let key in proxy</span><span class="pun">)</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">key</span><span class="pun">);</span><span class="pln"> </span><span class="com">// النتيجة: ‫test, عملية التكرار تعمل (3)</span></pre>

<p>
	انطلاقًا من عدم وجود اعتراضات سيُعاد توجيه جميع العمليات في الوسيط <code>proxy</code> إلى كائن الهدف <code>target</code>.
</p>

<ol>
<li>
		تحدد عملية الكتابة <code>proxy.test=‎</code> القيمة عند الهدف <code>target</code>، لاحظ السطر (1).
	</li>
	<li>
		تعيد عملية القراءة <code>proxy.test</code> القيمة من عند الهدف <code>target</code>. لاحظ السطر (2).
	</li>
	<li>
		تعيد عملية التكرار على الوسيط <code>proxy</code> القيم من الهدف <code>target</code>. لاحظ السطر (3).
	</li>
</ol>
<p>
	كما نرى، الوسيط <code>proxy</code> في هذه الحالة عبارة عن غِلاف شفاف حول الكائن الهدف <code>target</code>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="48029" href="https://academy.hsoub.com/uploads/monthly_2020_06/proxy.png.a61f33d0dc91ed43084b66b4ccd74954.png" rel=""><img alt="proxy.png" class="ipsImage ipsImage_thumbnailed" data-fileid="48029" data-unique="eiti4dmqi" src="https://academy.hsoub.com/uploads/monthly_2020_06/proxy.thumb.png.8c9b7ac659c5570159c93ac88fc4dd74.png"></a>
</p>

<p>
	لنُضف بعض الاعتراضات من أجل تفعيل المزيد من الإمكانيات.
</p>

<p>
	ما الذي يمكننا اعتراضه؟
</p>

<p>
	بالنسبة لمعظم العمليات على الكائنات، هناك ما يسمى "الدوالّ الداخلية" في مواصفات القياسية في اللغة، والّتي تصف كيفية عمل الكائن عند أدنى مستوى. فمثلًا الدالة الداخلية <code>[[Get]]</code> لقراءة خاصية ما، والدالة الداخلية <code>[[Set]]</code> لكتابة خاصية ما، وهكذا. وتستخدم هذه الدوال من قبل المواصفات فقط، ولا يمكننا استدعاؤها مباشرة من خلال أسمائها.
</p>

<p>
	اعتراضات الوسيط تتدخلّ عند استدعاء هذه الدوالّ. وهي مدرجة في <a href="https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots" rel="external nofollow">المواصفات القياسية</a> للوسيط وفي الجدول أدناه.
</p>

<p>
	يوجد اعتراض مخصص (دالّة معالجة) لكل دالّة داخلية في هذا الجدول: يمكننا إضافة اسم الدالّة إلى المعالج <code>handler</code> من خلال تمريرها كوسيط <code>new Proxy</code> لاعتراض العملية.
</p>

<table>
<thead><tr>
<th>
				الدالة الداخلية
			</th>
			<th>
				الدالة المعالجة
			</th>
			<th>
				ستعمل عند
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>[[Get]]</code>
			</td>
			<td>
				<code>get</code>
			</td>
			<td>
				قراءة خاصية
			</td>
		</tr>
<tr>
<td>
				<code>[[Set]]</code>
			</td>
			<td>
				<code>set</code>
			</td>
			<td>
				الكتابة على خاصية
			</td>
		</tr>
<tr>
<td>
				<code>[[HasProperty]]</code>
			</td>
			<td>
				<code>has</code>
			</td>
			<td>
				التأكد من وجود خاصية ما <code>in</code>
			</td>
		</tr>
<tr>
<td>
				<code>[[Delete]]</code>
			</td>
			<td>
				<code>deleteProperty</code>
			</td>
			<td>
				عملية الحذف <code>delete</code>
			</td>
		</tr>
<tr>
<td>
				<code>[[Call]]</code>
			</td>
			<td>
				<code>apply</code>
			</td>
			<td>
				استدعاء تابِع
			</td>
		</tr>
<tr>
<td>
				<code>[[Construct]]</code>
			</td>
			<td>
				<code>construct</code>
			</td>
			<td>
				عملية الإنشاء -الباني- <code>new</code>
			</td>
		</tr>
<tr>
<td>
				<code>[[GetPrototypeOf]]</code>
			</td>
			<td>
				<code>getPrototypeOf</code>
			</td>
			<td>
				<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf" rel="external nofollow">Object.getPrototypeOf</a>
			</td>
		</tr>
<tr>
<td>
				<code>[[SetPrototypeOf]]</code>
			</td>
			<td>
				<code>setPrototypeOf</code>
			</td>
			<td>
				<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf" rel="external nofollow">Object.setPrototypeOf</a>
			</td>
		</tr>
<tr>
<td>
				<code>[[IsExtensible]]</code>
			</td>
			<td>
				<code>isExtensible</code>
			</td>
			<td>
				<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible" rel="external nofollow">Object.isExtensible</a>
			</td>
		</tr>
<tr>
<td>
				<code>[[PreventExtensions]]</code>
			</td>
			<td>
				<code>preventExtensions</code>
			</td>
			<td>
				<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions" rel="external nofollow">Object.preventExtensions</a>
			</td>
		</tr>
<tr>
<td>
				<code>[[DefineOwnProperty]]</code>
			</td>
			<td>
				<code>defineProperty</code>
			</td>
			<td>
				<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty" rel="external nofollow">Object.defineProperty</a><br><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties" rel="external nofollow">Object.defineProperties</a>
			</td>
		</tr>
<tr>
<td>
				<code>[[GetOwnProperty]]</code>
			</td>
			<td>
				<code>getOwnPropertyDescriptor</code>
			</td>
			<td>
				<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor" rel="external nofollow">Object.getOwnPropertyDescriptor</a><br><code>for..in</code><br><code>Object.keys/values/entries</code>
			</td>
		</tr>
<tr>
<td>
				<code>[[OwnPropertyKeys]]</code>
			</td>
			<td>
				<code>ownKeys</code>
			</td>
			<td>
				<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames" rel="external nofollow">Object.getOwnPropertyNames</a><br><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols" rel="external nofollow">Object.getOwnPropertySymbols</a><br><code>for..in</code><br><code>Object/keys/values/entries</code>
			</td>
		</tr>
</tbody>
</table>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
}

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<p>
	<strong>تحذير</strong>: التعامل مع الثوابت
</p>

<p>
	تفرض علينا لغة جافاسكربت بعض الثوابت - الشروط الواجب تحقيقها من قِبل الدوالّ الداخلية والاعتراضات.
</p>

<p>
	معظمها للقيم المُعادة من الدوالّ:
</p>

<ul>
<li>
		يجب أن تعيد الدالّة <code>[[Set]]</code> النتيجة <code>true</code> إذا عُدلت القيمة بنجاح، وإلا ستُعيد <code>false</code>.
	</li>
	<li>
		يجب أن تعيد الدالّة <code>[[Delete]]</code> النتيجة <code>true</code> إذا حُذفت القيمة بنجاح، وإلا ستُعيد <code>false</code>.
	</li>
	<li>
		…وهكذا، سنرى المزيد من الأمثلة لاحقًا.
	</li>
</ul>
<p>
	هنالك بعض الثوابت الأخرى، مثل:
</p>

<ul>
<li>
		يجب أن تُعيد الدالة <code>[[GetPrototypeOf]]</code> المطبقة على كائن الوسيط نفس القيمة الّتي ستُعيدها الدلّة <code>[[GetPrototypeOf]]</code> المطبقة على كائن الهدف لكائن الوسيط، بتعبير آخر، يجب أن تعرض دائمًا قراءة النموذج الأولي لكائن الوسيط نفس قراءة النموذج الأولي لكائن الهدف.
	</li>
</ul>
<p>
	يمكن للاعتراضات التدخل في هذه العمليات ولكن لابد لها من اتباع هذه القواعد.
</p>

<p>
	تضمن هذه الثوابت السلوك الصحيح والمُتسق لمميّزات اللغة. وجميع هذه الثوابت موجودة في <a href="https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots" rel="external nofollow">المواصفات القياسية</a> للغة. وغالبًا لن تكسرَهم إن لم تنفذّ شيئًا غريبًا.
</p>

<p>
	لنرى كيف تعمل في مثال عملي.
</p>

<h2>
	إضافة اعتراض للقيم المبدئية
</h2>

<p>
	تعدُّ خاصيات القراءة / الكتابة من أكثر الاعتراضات شيوعًا.
</p>

<p>
	لاعتراض القراءة، يجب أن يكون لدى المعالج <code>handler</code> دالّة <code>get(target, property, receiver)‎</code>.
</p>

<p>
	وتُشغّل عند قراءة خاصية ما، وتكون الوسطاء:
</p>

<ul>
<li>
		<code>target</code> - هو كائن الهدف، والذي سيمرر كوسيط أول لِـ <code>new Proxy</code>،
	</li>
	<li>
		<code>property</code> - اسم الخاصية،
	</li>
	<li>
		<code>receiver</code> - إذا كانت الخاصية المستهدفة هي الجالِب (getter)، فإن <code>receiver</code> هو الكائن الذي سيُستخدم على أنه بديل للكلمة المفتاحية <code>this</code> في الاستدعاء. عادةً ما يكون هذا هو كائن <code>proxy</code> نفسه (أو كائن يرث منه، في حال ورِثنا من الوسيط). لا نحتاج الآن هذا الوسيط، لذلك سنشرحها بمزيد من التفصيل لاحقًا.
	</li>
</ul>
<p>
	لنستخدم الجالِب <code>get</code> لجلب القيم الافتراضية لكائن ما.
</p>

<p>
	سننشئ مصفوفة رقمية تُعيد القيمة <code>0</code> للقيم غير الموجودة.
</p>

<p>
	عادةً عندما نحاول الحصول على عنصر من مصفوفة، وكان هذا العنصر غير موجود، سنحصل على النتيجة غير معرّف <code>undefined</code>، لكننا هنا سنُغلف المصفوفة العادية داخل الوسيط والّذي سيعترضُ خاصية القراءة ويعيد <code>0</code> إذا لم تكُ الخاصية المطلوبة موجودة في المصفوفة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_11" style="">
<span class="pln">let numbers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">];</span><span class="pln">

numbers </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">numbers</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">prop in target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> target</span><span class="pun">[</span><span class="pln">prop</span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> </span><span class="com">// القيمة الافتراضية</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> numbers</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> numbers</span><span class="pun">[</span><span class="lit">123</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 0 (هذا العنصر غير موجود)</span></pre>

<p>
	كما رأينا، من السهل جدًا تنفيذ ذلك باعتراض الجالِب <code>get</code>.
</p>

<p>
	يمكننا استخدام الوسيط <code>proxy</code> لتنفيذ أي منطق للقيم "الافتراضية".
</p>

<p>
	تخيل أن لدينا قاموسًا (يربط القاموس مفاتيح مع قيم على هيئة أزواج) يربط العبارات مع ترجمتها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_13" style="">
<span class="pln">let dictionary </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">'Hello'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Hola'</span><span class="pun">,</span><span class="pln">
  </span><span class="str">'Bye'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Adiós'</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> dictionary</span><span class="pun">[</span><span class="str">'Hello'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// Hola</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> dictionary</span><span class="pun">[</span><span class="str">'Welcome'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// undefined</span></pre>

<p>
	مبدئيًا إن حاولنا الوصول إلى عبارة غير موجودة في القاموس فسيُعيد غير معرّف <code>undefined</code>، ولكن عمليًا إن ترك العبارة بدون مترجمة أفضل من <code>undefined</code>، لذا لنجعلها تُعيد العبارة بدون مترجمة بدلاً من <code>undefined</code>.
</p>

<p>
	لتحقيق ذلك، سنغلّف القاموس <code>dictionary</code> بالوسيط ليعترض عمليات القراءة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_15" style="">
<span class="pln">let dictionary </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">'Hello'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Hola'</span><span class="pun">,</span><span class="pln">
  </span><span class="str">'Bye'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Adiós'</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

dictionary </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">dictionary</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> phrase</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// اعترض خاصية القراءة من القاموس</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">phrase in target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// إن كانت موجودة في القاموس</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> target</span><span class="pun">[</span><span class="pln">phrase</span><span class="pun">];</span><span class="pln"> </span><span class="com">// أعد الترجمة</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// وإلا أعدها بدون ترجمة</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> phrase</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="com">// ‫ابحث عن عبارة عشوائية في القاموس!</span><span class="pln">
</span><span class="com">// بأسوء حالة سيُعيد العبارة غير مترجمة</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> dictionary</span><span class="pun">[</span><span class="str">'Hello'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// Hola</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> dictionary</span><span class="pun">[</span><span class="str">'Welcome to Proxy'</span><span class="pun">]);</span><span class="pln"> </span><span class="com">// Welcome to Proxy (يعيد العبارة بدون ترجمة)</span></pre>

<p>
	لاحظ كيف أن الوسيط يعد الكتابة على المتغير الافتراضي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_19" style="">
<span class="pln">dictionary </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">dictionary</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...);</span></pre>

<p>
	يجب على الوسيط استبدال كائن الهدف بالكامل في كل مكان. ولا يجب أن يشير أي شيء للكائن الهدف بعد أن يستبدلهُ الوسيط. وإلا فذلك سيُحدثُ فوضى عارمة.
</p>

<h2>
	تدقيق المدخلات باعتراض الضابِط "set"
</h2>

<p>
	لنفترض أننا نريد إنشاء مصفوفة مخصصة للأرقام فقط. وإن أضيفت قيمة من نوع آخر، يجب أن يظهر خطأ.
</p>

<p>
	يُشغّل اعتراض الضابِط <code>set</code> عند الكتابة على الخاصية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_21" style="">
<span class="kwd">set</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> property</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)‎</span></pre>

<ul>
<li>
		<code>target</code> - الكائن الهدف، الذي سنمرره كوسيط أول <code>new Proxy</code>،
	</li>
	<li>
		<code>property</code> - اسم الخاصية،
	</li>
	<li>
		<code>value</code> - قيمة الخاصية،
	</li>
	<li>
		<code>receiver</code> - مشابه للدالّة <code>get</code>، ولكن هنا لضبط الخاصيات فقط.
	</li>
</ul>
<p>
	يجب أن يُعيد الاعتراض <code>set</code> القيمة <code>true</code> إذا نجح ضبط القيمة في الخاصية، وإلا يعيد القيمة <code>false</code> (يُشغّل خطأ من نوع <code>TypeError</code>).
</p>

<p>
	لنأخذ مثالًا عن كيفية استخدامها للتحقق من القيم الجديدة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_23" style="">
<span class="pln">let numbers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">

numbers </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">numbers</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">
  </span><span class="kwd">set</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> val</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// لاعتراض خاصية ضبط القيمة</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">typeof</span><span class="pln"> val </span><span class="pun">==</span><span class="pln"> </span><span class="str">'number'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      target</span><span class="pun">[</span><span class="pln">prop</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> val</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

numbers</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln"> </span><span class="com">// أضيفت بنجاح</span><span class="pln">
numbers</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln"> </span><span class="com">// أضيفت بنجاح</span><span class="pln">
alert</span><span class="pun">(</span><span class="str">"Length is: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> numbers</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 2</span><span class="pln">

numbers</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="str">"test"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫TypeError (الدالّة 'set' في الوسيط أعادت القيمة false)</span><span class="pln">

alert</span><span class="pun">(</span><span class="str">"This line is never reached (error in the line above)"</span><span class="pun">);</span></pre>

<p>
	لاحظ أن الدوال المدمجة للمصفوفات ما تزال تعمل! إذ تضاف القيم عن طريق <code>push</code>. فتزداد خاصية <code>length</code> تلقائيًا عند إضافة هذه القيم. لذا فإن الوسيط لن يكسر أي شيء.
</p>

<p>
	يجب علينا ألا نعيد كتابة الدوالّ المُدمجة للمصفوفات الّتي تضيف القيم مثل <code>push</code> و<code>unshift</code>، وما إلى ذلك، إن كان هدفنا إضافة عمليات تحقق، لأنهم يستخدمون داخليًا دالّة <code>[[Set]]</code> والّتي ستُعترضُ من قِبل الوسيط.
</p>

<p>
	إذن الشيفرة نظيفة ومختصرة.
</p>

<p>
	لا تنسَ أن تعيد القيمة <code>true</code> عند نجاح عملية الكتابة. كما ذكر أعلاه، هناك ثوابت ستعقّد. بالنسبة للضابط <code>set</code>، يجب أن يُرجع <code>true</code> عند نجاح عملية الكتابة. إذا نسينا القيام بذلك أو إعادة أي قيمة زائفة، فإن العملية تؤدي إلى خطأ <code>TypeError</code>.
</p>

<h2>
	التكرار باستخدام تابع "ownKeys" و"getOwnPropertyDescriptor"
</h2>

<p>
	إن الدالة <code>Object.keys</code> وحلقة التكرار <code>for..in</code> ومعظم الطرق الأخرى الّتي تستعرض خاصيات الكائن، وتستخدم الدالّة الداخلية <code>[[OwnPropertyKeys]]</code> للحصول على قائمة بالخاصيات يمكننا اعتراضها من خلال <code>ownKeys</code>.
</p>

<p>
	تختلف هذه الدوال وحلقات التكرار على الخاصيات تحديدًا في:
</p>

<ul>
<li>
		<code>Object.getOwnPropertyNames (obj)‎</code>: تُعيد الخاصيات غير الرمزية.
	</li>
	<li>
		<code>Object.getOwnPropertySymbols (obj)‎</code>: تُعيد الخاصيات الرمزية.
	</li>
	<li>
		<code>Object.keys/values()‎</code>: تعيد الخاصيات/القيم غير الرمزية والتي تحمل راية قابلية الاحصاء <code>enumerable</code> (شرحنا في مقال سابق ما هي <a href="https://academy.hsoub.com/programming/javascript/%D8%B1%D8%A7%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AE%D8%A7%D8%B5%D9%8A%D8%A7%D8%AA-%D9%88%D9%88%D8%A7%D8%B5%D9%90%D9%81%D8%A7%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r881/" rel="">رايات الخواص وواصفاتها</a> يمكنك الاطلاع عليه لمزيد من التفاصيل).
	</li>
	<li>
		حلقات <code>for..in</code>: تمرّ على الخاصيات غير الرمزية التي تحمل راية قابلية الاحصاء <code>enumerable</code>، وكذلك تمرّ على خاصيات النموذج الأولي (prototype).
	</li>
</ul>
<p>
	… لكن كلهم يبدأون بهذه القائمة.
</p>

<p>
	في المثال أدناه ، نستخدم اعتراض <code>ownKeys</code> لجعل حلقة<code>for..in</code> تمر على <code>user</code>، وكذلك<code>Object.keys</code> و<code>Object.values</code>، وتتخطى الخاصيات التي تبدأ بشرطة سفلية <code>_</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_25" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">,</span><span class="pln">
  age</span><span class="pun">:</span><span class="pln"> </span><span class="lit">30</span><span class="pun">,</span><span class="pln">
  _password</span><span class="pun">:</span><span class="pln"> </span><span class="str">"***"</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

user </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  ownKeys</span><span class="pun">(</span><span class="pln">target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">(</span><span class="pln">target</span><span class="pun">).</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">key </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">!</span><span class="pln">key</span><span class="pun">.</span><span class="pln">startsWith</span><span class="pun">(</span><span class="str">'_'</span><span class="pun">));</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="com">// ‫الاعتراض "ownKeys" سيزيل الخاصّية ‎_password</span><span class="pln">
</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let key in user</span><span class="pun">)</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">key</span><span class="pun">);</span><span class="pln"> </span><span class="com">// name, then: age</span><span class="pln">

</span><span class="com">// ‫نفس التأثير سيحدث على هذه التوابع:</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// name,age</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">values</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// John,30</span></pre>

<p>
	حتى الآن، لا يزال مثالنا يعمل وفق المطلوب.
</p>

<p>
	على الرغم من ذلك، إذا أضفنا خاصية ما غير موجودة في الكائن الأصلي وذلك بإرجاعها من خلال الوسيط، فلن يعيدها التابع <code>Object.keys</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_27" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">};</span><span class="pln">

user </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  ownKeys</span><span class="pun">(</span><span class="pln">target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[</span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'b'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'c'</span><span class="pun">];</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// &lt;empty&gt;</span></pre>

<p>
	هل تسأل نفسك لماذا؟ السبب بسيط: تُرجع الدالّة <code>Object.keys</code> فقط الخاصيات الّتي تحمل راية قابلية الإحصاء <code>enumerable</code>. وهي تحقق من رايات الخاصيات لديها باستدعاء الدالّة الداخلية <code>[[GetOwnProperty]]</code> لكل خاصية للحصول على <a href="https://academy.hsoub.com/programming/javascript/%D8%B1%D8%A7%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AE%D8%A7%D8%B5%D9%8A%D8%A7%D8%AA-%D9%88%D9%88%D8%A7%D8%B5%D9%90%D9%81%D8%A7%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r881/" rel="">واصِفها</a>. وهنا، نظرًا لعدم وجود الخاصّية، فإن واصفها فارغ، ولا يوجد راية قابلية الإحصاء <code>enumerable</code>، وبناءً عليه تخطت الدالّة الخاصّية.
</p>

<p>
	من أجل أن ترجع الدالّة <code>Object.keys</code> الخاصية، فيجب أن تكون إما موجودة في الكائن الأصلي، وتحمل راية قابلية الإحصاء <code>enumerable</code>، أو يمكننا وضع اعتراض عند استدعاء الدالة الداخلية <code>[[GetOwnProperty]]</code> (اعتراض <code>getOwnPropertyDescriptor</code> سيحقق المطلوب)، وإرجاع واصف لخاصية قابلية الإحصاء بالإيجاب هكذا <code>enumerable: true</code>.
</p>

<p>
	إليك المثال ليتوضح الأمر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_29" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">};</span><span class="pln">

user </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  ownKeys</span><span class="pun">(</span><span class="pln">target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// يستدعى مرة واحدة عند طلب قائمة الخاصيات</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[</span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'b'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'c'</span><span class="pun">];</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">

  getOwnPropertyDescriptor</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// تستدعى من أجل كلّ خاصّية</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      enumerable</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
      configurable</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
      </span><span class="com">/* ...يمكننا إضافة رايات أخرى مع القيم المناسبة لها..." */</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">});</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// a, b, c</span></pre>

<p>
	نعيد مرة أخرى: نضيف اعتراض <code>[[GetOwnProperty]]</code> إذا كانت الخاصّية غير موجودة في الكائن الأصلي.
</p>

<h2>
	الخاصيات المحمية والاعتراض "deleteProperty" وغيره
</h2>

<p>
	هناك إجماع كبير في مجتمع المطورين بأن الخاصيات والدوالّ المسبوقة بشرطة سفلية <code>_</code> هي للاستخدام الداخلي. ولا ينبغي الوصول إليها من خارج الكائن.
</p>

<p>
	هذا ممكن تقنيًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_31" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">,</span><span class="pln">
  _password</span><span class="pun">:</span><span class="pln"> </span><span class="str">"secret"</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="com">// لاحظ أن في الوضع الطبيعي يمكننا الوصول لها</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">_password</span><span class="pun">);</span><span class="pln"> </span><span class="com">// secret</span></pre>

<p>
	لنستخدم الوسيط لمنع الوصول إلى الخاصيات الّتي تبدأ بشرطة سفلية <code>_</code>.
</p>

<p>
	سنحتاج استخدام الاعتراضات التالية:
</p>

<ul>
<li>
		<code>get</code> لإلقاء خطأ عند قراءة الخاصية،
	</li>
	<li>
		<code>set</code> لإلقاء خطأ عند الكتابة على الخاصية،
	</li>
	<li>
		<code>deleteProperty</code> لإلقاء خطأ عند حذف الخاصية،
	</li>
	<li>
		<code>ownKeys</code> لاستبعاد الخصائص التي تبدأ بشرطة سفلية <code>_</code> من حلقة <code>for..in</code>، والدوالّ التي تستعرض الخاصيات مثل: <code>Object.keys</code>.
	</li>
</ul>
<p>
	هكذا ستكون الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_33" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">,</span><span class="pln">
  _password</span><span class="pun">:</span><span class="pln"> </span><span class="str">"***"</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

user </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">prop</span><span class="pun">.</span><span class="pln">startsWith</span><span class="pun">(</span><span class="str">'_'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Access denied"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    let value </span><span class="pun">=</span><span class="pln"> target</span><span class="pun">[</span><span class="pln">prop</span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">typeof</span><span class="pln"> value </span><span class="pun">===</span><span class="pln"> </span><span class="str">'function'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> value</span><span class="pun">.</span><span class="pln">bind</span><span class="pun">(</span><span class="pln">target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> value</span><span class="pun">;</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="kwd">set</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> val</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// لاعتراض عملية الكتابة على الخاصّية</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">prop</span><span class="pun">.</span><span class="pln">startsWith</span><span class="pun">(</span><span class="str">'_'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Access denied"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      target</span><span class="pun">[</span><span class="pln">prop</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> val</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  deleteProperty</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// لاعتراض عملية حذف الخاصّية</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">prop</span><span class="pun">.</span><span class="pln">startsWith</span><span class="pun">(</span><span class="str">'_'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Access denied"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">delete</span><span class="pln"> target</span><span class="pun">[</span><span class="pln">prop</span><span class="pun">];</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  ownKeys</span><span class="pun">(</span><span class="pln">target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// لاعتراض رؤية الخاصية من خلال الحلقات أو الدوالّ</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">(</span><span class="pln">target</span><span class="pun">).</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">key </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">!</span><span class="pln">key</span><span class="pun">.</span><span class="pln">startsWith</span><span class="pun">(</span><span class="str">'_'</span><span class="pun">));</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="com">// ‫الاعتراض "get" سيمنعُ قراءة الخاصّية ‎_password</span><span class="pln">
</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">_password</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Error: Access denied</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="com">// ‫  الاعتراض "set" سيمنع الكتابة على الخاصّية ‎_password</span><span class="pln">
</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  user</span><span class="pun">.</span><span class="pln">_password </span><span class="pun">=</span><span class="pln"> </span><span class="str">"test"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// Error: Access denied</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="com">// ‫  الاعتراض "deleteProperty" سيمنعُ حذف الخاصّية ‎_password</span><span class="pln">
</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">delete</span><span class="pln"> user</span><span class="pun">.</span><span class="pln">_password</span><span class="pun">;</span><span class="pln"> </span><span class="com">// Error: Access denied</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="com">// ‫  الاعتراض "ownKeys" سيمنعُ إمكانية رؤية الخاصّية ‎_password في الحلقات</span><span class="pln">
</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let key in user</span><span class="pun">)</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">key</span><span class="pun">);</span><span class="pln"> </span><span class="com">// name</span></pre>

<p>
	لاحظ التفاصيل المهمة في الاعتراض <code>get</code>، وذلك في السطر <code>(*)</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_37" style="">
<span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  let value </span><span class="pun">=</span><span class="pln"> target</span><span class="pun">[</span><span class="pln">prop</span><span class="pun">];</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">typeof</span><span class="pln"> value </span><span class="pun">===</span><span class="pln"> </span><span class="str">'function'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> value</span><span class="pun">.</span><span class="pln">bind</span><span class="pun">(</span><span class="pln">target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> value</span><span class="pun">;</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لماذا نحتاج لدالّة لاستدعاء <code>value.bind(target)‎</code>؟ وسبب ذلك هو أن دوال الكائن مثل: <code>user.checkPassword()‎</code> يجب تحافظ على إمكانية الوصول للخاصّية <code>‎_password</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_39" style="">
<span class="pln">user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  checkPassword</span><span class="pun">(</span><span class="pln">value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ‫دوال الكائن يجب أن تحافظ على إمكانية الوصول للخاصية ‎_password</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> value </span><span class="pun">===</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">_password</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تستدعي الدالة <code>user.checkPassword()‎</code> وسيط الكائن <code>user</code> ليحلّ محلّ <code>this</code> (الكائن قبل النقطة يصبح بدل <code>this</code>)، لذلك عندما نحاول الوصول إلى الخاصّية <code>this._password</code>، سينُشّط الاعتراض <code>get</code> ( والّذي يُشغلّ عند قراءة خاصية ما) ويلقي الخطأ.
</p>

<p>
	لذلك نربط سياق الدوال الخاصة بالكائن مع دوالّ الكائن الأصلي، أيّ الكائن الهدف <code>target</code> لاحظ السطر <code>(*)</code>. بعد ذلك، الاستدعاءات المستقبلية ستستخدم <code>target</code> بدلّ <code>this</code>، دون الاعتراضات.
</p>

<p>
	هذا الحل ليس مثاليًا ولكنه يفي بالغرض، ولكن يمكن لدالّةٍ ما أن تُمرّر الكائن غير المغَلّف بالوسيط (أي الكائن الأصلي) إلى مكان آخر، وبذلك ستخرّب الشيفرةولن نستطع الإجابة على أسئلة مثل: أين يستخدم الكائن الأصلي؟ وأين يستخدم الكائن الوسيط؟
</p>

<p>
	أضف إلى ذلك، يمكن للكائن أن يغلف بأكثر من وسيط (تضيف عدة وسطاء "تعديلات" مختلفة على الكائن الأصلي)، وإن مرّرنا كائن غير مغلّف بالوسيط إلى دالّة ما، ستكون هناك عواقب غير متوقعة.
</p>

<p>
	لذا، لا ينبغي استخدام هذا الوسيط في كل الحالات.
</p>

<p>
	<strong>ملاحظة</strong>: الخاصيات الخاصة بالصنف
</p>

<p>
	تدعم محركات جافاسكربت الحديثة الخاصيات الخاصّة بالأصناف، وتكون مسبوقة بـ <code>#</code>. تطرقنا لها سابقًا في مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AE%D8%A7%D8%B5%D9%8A%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-%D8%A7%D9%84%D8%AE%D8%A7%D8%B5%D8%A9-%D9%88%D8%A7%D9%84%D9%85%D8%AD%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r904/" rel="">الخصائص والتوابع الخاصّة والمحمية</a>. وبدون استخدام أيّ الوسيط.
</p>

<p>
	على الرغم من ذلك هذه الخاصيات لها مشاكلها الخاصة أيضًا. وتحديدًا، مشكلة عدم إمكانية توريث هذه الخاصيات.
</p>

<h2>
	استخدام الاعتراض "In range" مع "has"
</h2>

<p>
	لنرى مزيدًا من الأمثلة.
</p>

<p>
	لدينا الكائن <code>range</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_41" style="">
<span class="pln">let range </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  start</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  end</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	نريد استخدام المعامل <code>in</code> للتحقق من أن الرقم موجود في <code>range</code>.
</p>

<p>
	إن الاعتراض <code>has</code> سيعترض استدعاءات <code>in</code>.
</p>

<p>
	<code>has(target, property)‎</code>
</p>

<ul>
<li>
		<code>target - هو الكائن الهدف، الذي سيمرر كوسيط أول</code>new Proxy`،
	</li>
	<li>
		<code>property</code> - اسم الخاصية
	</li>
</ul>
<p>
	إليك المثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_43" style="">
<span class="pln">let range </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  start</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  end</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

range </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">range</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  has</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> prop </span><span class="pun">&gt;=</span><span class="pln"> target</span><span class="pun">.</span><span class="pln">start </span><span class="pun">&amp;&amp;</span><span class="pln"> prop </span><span class="pun">&lt;=</span><span class="pln"> target</span><span class="pun">.</span><span class="pln">end</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

alert</span><span class="pun">(</span><span class="lit">5</span><span class="pln"> in range</span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span><span class="pln">
alert</span><span class="pun">(</span><span class="lit">50</span><span class="pln"> in range</span><span class="pun">);</span><span class="pln"> </span><span class="com">// false</span></pre>

<p>
	<a href="https://ar.wikipedia.org/wiki/Syntactic_sugar" rel="external nofollow">تجميلٌ لغويٌّ</a> رائع، أليس كذلك؟ وتنفيذه بسيط جدًا.
</p>

<h2>
	تغليف التوابع باستخدام "apply"
</h2>

<p>
	يمكننا أيضًا تغليف دالّة ما باستخدام كائن الوسيط.
</p>

<p>
	يعالج الاعتراض <code>apply(target, thisArg, args)‎</code> استدعاء كائن الوسيط كتابع:
</p>

<ul>
<li>
		<code>target</code>: الكائن الهدف (التابع - أو الدالّة - هي كائن في لغة جافا سكربت)،
	</li>
	<li>
		<code>thisArg</code>: قيمة <code>this</code>.
	</li>
	<li>
		<code>args</code>: قائمة الوسطاء.
	</li>
</ul>
<p>
	فمثلًا، لنتذكر المُزخرف <code>delay(f, ms)‎</code>، الذي أنشأناه في مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%B1%D8%A8%D8%B7-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-function-binding-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r879/" rel="">المزخرفات والتمرير</a>.
</p>

<p>
	في تلك المقالة أنشأناه بدون استخدام الوسيط. عند استدعاء الدالة <code>delay(f, ms)‎</code> ستُعيد دالّة أخرى، والّتي بدورها ستوجه جميع الاستدعاءات إلى <code>f</code> بعد <code>ms</code> ملّي ثانية.
</p>

<p>
	إليك التطبيق المثال بالاعتماد على التوابع:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_45" style="">
<span class="kwd">function</span><span class="pln"> delay</span><span class="pun">(</span><span class="pln">f</span><span class="pun">,</span><span class="pln"> ms</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ‫يعيد المغلف والذي بدوره سيوجه الاستدعاءات إلى f بعد انتهاء مهلة زمنية معينة</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">
    setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> f</span><span class="pun">.</span><span class="pln">apply</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">,</span><span class="pln"> arguments</span><span class="pun">),</span><span class="pln"> ms</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> sayHi</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">user</span><span class="pun">}!`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// ‫بعد عملية التغليف استدعي الدالّة sayHi بعد 3 ثواني</span><span class="pln">
sayHi </span><span class="pun">=</span><span class="pln"> delay</span><span class="pun">(</span><span class="pln">sayHi</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">);</span><span class="pln">

sayHi</span><span class="pun">(</span><span class="str">"John"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Hello, John! (بعد 3 ثواني)</span></pre>

<p>
	كما رأينا، غالبًا ستعمل هذه الطريقة وفق المطلوب. تُنفذّ الدالّة المغلفة في السطر <code>(*)</code> استدعاءً بعد انتهاء المهلة.
</p>

<p>
	لكن دالة المغلف لا تعيد توجيه خاصيات القراءة أو الكتابة للعمليات أو أيّ شيء آخر. بعد عملية التغليف، يُفقدُ إمكانية الوصول لخاصيات التوابع الأصلية، مثل: <code>name</code> و<code>length</code> وغيرها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_47" style="">
<span class="kwd">function</span><span class="pln"> delay</span><span class="pun">(</span><span class="pln">f</span><span class="pun">,</span><span class="pln"> ms</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> f</span><span class="pun">.</span><span class="pln">apply</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">,</span><span class="pln"> arguments</span><span class="pun">),</span><span class="pln"> ms</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> sayHi</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">user</span><span class="pun">}!`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">sayHi</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫1 (طول الدالة هو عدد الوسطاء في تعريفها)</span><span class="pln">
sayHi </span><span class="pun">=</span><span class="pln"> delay</span><span class="pun">(</span><span class="pln">sayHi</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">sayHi</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫0 (إن عدد وسطاء عند تعريف المغلّف هو 0)</span></pre>

<p>
	في الحقيقة إن إمكانيات الوسيط <code>Proxy</code> أقوى بكثير من ذلك، إذ إنه يعيد توجيه كل شيء إلى الكائن الهدف.
</p>

<p>
	لنستخدم الوسيط <code>Proxy</code> بدلاً من الدالّة المُغلِّفة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_49" style="">
<span class="kwd">function</span><span class="pln"> delay</span><span class="pun">(</span><span class="pln">f</span><span class="pun">,</span><span class="pln"> ms</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">f</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    apply</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> thisArg</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> target</span><span class="pun">.</span><span class="pln">apply</span><span class="pun">(</span><span class="pln">thisArg</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">),</span><span class="pln"> ms</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> sayHi</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">user</span><span class="pun">}!`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

sayHi </span><span class="pun">=</span><span class="pln"> delay</span><span class="pun">(</span><span class="pln">sayHi</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">sayHi</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫1 (*) سيعيد الوسيط توجيه عملية "get length" إلى الهدف</span><span class="pln">

sayHi</span><span class="pun">(</span><span class="str">"John"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Hello, John! (بعد 3 ثواني)</span></pre>

<p>
	نلاحظ أن النتيجة نفسها، ولكن الآن ليس مجرد استدعاءات فقط، وإنما كل العمليات في الوسيط يُعاد توجيها إلى التوابع الأصلية. لذلك سيعيد الاستدعاء <code>sayHi.length</code> النتيجة الصحيحة بعد التغليف في السطر <code>(*)</code>.
</p>

<p>
	وبذلك حصلنا على مُغلِّف أغنى بالمميزات من الطريقة السابقة.
</p>

<p>
	هنالك العديد من الاعتراضات الأخرى: يمكنك العودة لبداية المقال لقراءة القائمة الكاملة للاعتراضات. كما أن طريقة استخدامها مشابه كثيرًا لما سبق.
</p>

<h2>
	الانعكاس
</h2>

<p>
	الانعكاس <code>Reflect</code> هو عبارة عن كائن مضمّن في اللغة يبسط إنشاء الوسيط <code>Proxy</code>.
</p>

<p>
	ذكرنا سابقًا أن الدوالّ الداخلية، مثل: <code>[[Get]]</code> و<code>[[Set]]</code> وغيرها مخصصة للاستخدام في مواصفات اللغة فقط، ولا يمكننا استدعاؤها مباشرة.
</p>

<p>
	يمكن لكائن المنعكس <code>Reflect</code> من فعل ذلك إلى حد ما. إذ أن الدوالّ الخاصة به عبارة مُغلِّفات صغيرة حول الدوالّ الداخلية.
</p>

<p>
	فيما يلي أمثلة للعمليات واستدعاءات المنعكس <code>Reflect</code> الّتي ستُؤدي نفس المهمة:
</p>

<table>
<thead><tr>
<th>
				الدالة الداخلية
			</th>
			<th>
				الدالة المقابلة في المنعكس
			</th>
			<th>
				العملية
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>[[Get]]</code>
			</td>
			<td>
				<code>Reflect.get(obj, prop)</code>‎
			</td>
			<td>
				<code>obj[prop]</code>‎
			</td>
		</tr>
<tr>
<td>
				<code>[[Set]]</code>
			</td>
			<td>
				<code>Reflect.set(obj, prop, value)</code>‎
			</td>
			<td>
				<code>obj[prop] = value</code>
			</td>
		</tr>
<tr>
<td>
				<code>[[Delete]]</code>
			</td>
			<td>
				<code>Reflect.deleteProperty(obj, prop)</code>‎
			</td>
			<td>
				<code>delete obj[prop]</code>‎
			</td>
		</tr>
<tr>
<td>
				<code>[[Construct]]</code>
			</td>
			<td>
				<code>Reflect.construct(F, value)</code>‎
			</td>
			<td>
				<code>new F(value)</code>‎
			</td>
		</tr>
<tr>
<td>
				…
			</td>
			<td>
				…
			</td>
			<td>
				…
			</td>
		</tr>
</tbody>
</table>
<p>
	فمثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_51" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">

</span><span class="typ">Reflect</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> </span><span class="str">'name'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'John'</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="com">// John</span></pre>

<p>
	‎ تحديدًا، يتيح لنا المنعكس <code>Reflect</code> استدعاء العمليات (<code>new</code>, <code>delete</code>…‎) كتوابع هكذا (‎<code>Reflect.construct</code>,<code>Reflect.deleteProperty</code>, …‎). وهذه الامكانيات مثيرة للاهتمام، ولكن هنالك شيء آخر مهم.
</p>

<p>
	لكل دالّة داخلية، والّتي يمكننا تتبعها من خلال الوسيط <code>Proxy</code>، يوجد دالّة مقابلة لها في المنعكس <code>Reflect</code>، بنفس الاسم والوسطاء أي مشابه تمامًا للاعتراض في الوسيط <code>Proxy</code>.
</p>

<p>
	لذا يمكننا استخدام المنعكس <code>Reflect</code> لإعادة توجيه عملية ما إلى الكائن الأصلي.
</p>

<p>
	في هذا المثال، سيكون كلًا من الاعتراضين <code>get</code> و<code>set</code> شفافين (كما لو أنهما غير موجودين) وسيُوجهان عمليات القراءة والكتابة إلى الكائن، مع إظهار رسالة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_53" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">,</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

user </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(`</span><span class="pln">GET $</span><span class="pun">{</span><span class="pln">prop</span><span class="pun">}`);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Reflect</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (1)</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="kwd">set</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> val</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(`</span><span class="pln">SET $</span><span class="pun">{</span><span class="pln">prop</span><span class="pun">}=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">val</span><span class="pun">}`);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Reflect</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> val</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (2)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

let name </span><span class="pun">=</span><span class="pln"> user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">;</span><span class="pln"> </span><span class="com">// shows "GET name"</span><span class="pln">
user</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Pete"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// shows "SET name=Pete"</span></pre>

<p>
	في الشيفرة السابقة:
</p>

<ul>
<li>
		<code>Reflect.get</code>: يقرأ خاصية الكائن.
	</li>
	<li>
		<code>Reflect.set</code>: يكتب خاصية الكائن، سيُعيد <code>true</code> إن نجحت، وإلا سيُعيد <code>false</code>.
	</li>
</ul>
<p>
	أي أن كل شيء بسيط: إذا كان الاعتراض يُعيد توجيه الاستدعاء إلى الكائن، فيكفي استدعاء <code>Reflect.&lt;method&gt;‎</code> بنفس الوسطاء.
</p>

<p>
	في معظم الحالات ، يمكننا فعل الشيء نفسه بدون <code>Reflect</code>، على سبيل المثال، يمكن استبدال <code>Reflect.get(target, prop, receiver)‎</code> بـ <code>target[prop]‎</code>. يوجد فروقٍ بينهم ولكن لا تكاد تذكر.
</p>

<h3>
	استخدام الوسيط مع الجالِب
</h3>

<p>
	لنرى مثالاً يوضح لماذا <code>Reflect.get</code> أفضل. وسنرى أيضًا سبب وجود الوسيط الرابع في دوالّ <code>get/set</code>، تحديدًا <code>receiver</code> والّذي لم نستخدمه بعد.
</p>

<p>
	لدينا كائن <code>user</code> مع خاصية <code>‎_name</code> وجالب مخصص لها.
</p>

<p>
	لنُغلِّفه باستخدام الوسيط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_55" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  _name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Guest"</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">get</span><span class="pln"> name</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">_name</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

let userProxy </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> target</span><span class="pun">[</span><span class="pln">prop</span><span class="pun">];</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">userProxy</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Guest</span></pre>

<p>
	إن الاعتراض <code>get</code> في هذا المثال "شفاف"، فهو يعيد الخاصية الأصلية ولا يفعل أي شيء آخر. هذا يكفي لمثالنا.
</p>

<p>
	يبدو أن كل شيء على ما يرام. لكن لنُزد تعقيد المثال قليلًا.
</p>

<p>
	بعد وراثة كائن <code>admin</code> من الكائن <code>user</code>، نلاحظ السلوك الخاطئ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_57" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  _name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Guest"</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">get</span><span class="pln"> name</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">_name</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

let userProxy </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> target</span><span class="pun">[</span><span class="pln">prop</span><span class="pun">];</span><span class="pln"> </span><span class="com">// (*) target = user</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

let admin </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  __proto__</span><span class="pun">:</span><span class="pln"> userProxy</span><span class="pun">,</span><span class="pln">
  _name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Admin"</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="com">// ‫نتوقع ظهور الكلمة: Admin</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">admin</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫النتيجة: Guest (لماذا؟)</span></pre>

<p>
	إن قراءة <code>admin.name</code> يجب أن تُعيد كلمة <code>"Admin"</code> وليس <code>"Guest"</code>!
</p>

<p>
	ما الذي حدث؟ لعلنا أخطأنا بشيء ما في عملية الوراثة؟
</p>

<p>
	ولكن إذا أزلنا كائن الوسيط، فسيكون كل شيء على ما يرام.
</p>

<p>
	إذًا المشكلة الحقيقية في الوسيط، تحديدًا في السطر <code>(*)</code>.
</p>

<ol>
<li>
		<p>
			عندما نقرأ خاصّية الاسم <code>admin.name</code> من كائن <code>admin</code>، لا يحتوي كائن <code>admin</code> على هذه الخاصية، وبذلك ينتقل للبحث عنها في النموذج الأولي الخاص به.
		</p>
	</li>
	<li>
		<p>
			النموذج الأولي الخاص به هو <code>userProxy</code>.
		</p>
	</li>
	<li>
		<p>
			عند قراءة الخاصية <code>name</code> من الوسيط، سيُشغّل اعتراض <code>get</code> الخاص به، وسيُعيدih من الكائن الأصلي هكذا <code>target[prop]‎</code> في السطر <code>(*)</code>.
		</p>

		<p>
			يؤدي الاستدعاء <code>target [prop]‎</code>، عندما يكون قيمة <code>prop</code> هي الجالب (getter)، سيؤدي ذلك لتشغيل الشيفرة بالسياقِ <code>this = target</code>. لذلك تكون النتيجة <code>this._name</code> من الكائن الأصلي للهدف <code>target</code>، أي: من <code>user</code>.
		</p>
	</li>
</ol>
<p>
	لإصلاح مثل هذه المواقف، نحتاج إلى <code>receiver</code>، الوسيط الثالث للاعتراض <code>get</code>. إذ سيُحافظ على قيمة <code>this</code> الصحيحة لتُمرر بعد ذلك إلى الجالِب (getter). في حالتنا تكون قيمتها هي <code>admin</code>.
</p>

<p>
	كيفية يمرر سياق الاستدعاء للحصول الجالِب الصحيح؟ بالنسبة للتابع العادي، يمكننا استخدام <code>call/apply</code>، ولكن بالنسبة للجالِب فلن يستدعى بهذه الطريقة.
</p>

<p>
	لنستخدم الدالّة <code>Reflect.get</code> والتي يمكنها القيام بذلك. وكل شيء سيعمل مثلما نريد.
</p>

<p>
	وإليك الشكل الصحيح:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_59" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  _name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Guest"</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">get</span><span class="pln"> name</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">_name</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

let userProxy </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// receiver = admin</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Reflect</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">


let admin </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  __proto__</span><span class="pun">:</span><span class="pln"> userProxy</span><span class="pun">,</span><span class="pln">
  _name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Admin"</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">admin</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Admin</span></pre>

<p>
	الآن يحافظ <code>receiver</code> بمرجع لقيمة <code>this</code> الصحيحة (وهي <code>admin</code>)، والّتي ستُمرر من خلال <code>Reflect.get</code> في السطر <code>(*)</code>.
</p>

<p>
	يمكننا إعادة كتابة الاعتراض بطريقة أقصر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_61" style="">
<span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Reflect</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(...</span><span class="pln">arguments</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	استدعاءات المنعكس <code>Reflect</code> لها نفس أسماء اعتراضات الوسيط وتقبل نفس وسطائه أيضًا. إذ صُمّمت خصيصًا لهذا الغرض.
</p>

<p>
	لذا ، فإن استخدام المنعكس <code>return Reflect...‎</code> يزودنا بالأمان والثقة لتوجيه العملية والتأكد تمامًا من أننا لن ننسَ أي شيء متعلقٌ بها.
</p>

<h2>
	قيود الوسيط
</h2>

<p>
	لدى الوسيط طريقة فريدة لتغيير أو تعديل سلوك الكائنات الموجودة عند أدنى مستوى. ومع ذلك، هذه الطريقة ليست مثالية. وإنما هناك قيود.
</p>

<h3>
	كائنات مضمّنة: فتحات داخلية
</h3>

<p>
	تستخدم العديد من الكائنات المضمنة، مثل الكائنات <code>Map</code> و<code>Set</code> و<code>Date</code> و<code>Promise</code> وغيرها ما يسمى بـ" الفتحات الداخلية ".
</p>

<p>
	وهي مشابهة للخاصيات، لكنها محفوظة للأغراض داخلية فقط، وللمواصفات القياسية للغة فقط. فمثلًا تخزن <code>Map</code> العناصر في الفتحة الداخلية <code>[[MapData]]</code>. وتستطيع الدوال المُضمّنة الوصول إليها مباشرةً، وليس عبر الدوالّ الداخلية مثل <code>[[Get]]/[[Set]]</code>. لذا فإن الوسيط <code>Proxy</code> لا يمكنه اعتراض ذلك.
</p>

<p>
	ولكن ما سبب اهتمامنا بذلك؟ إنها بكلّ الأحوال داخلية!
</p>

<p>
	حسنًا ، إليك المشكلة. بعد أن يغلف الكائن المضمن مثل: Map باستخدام الوسيط، لن يمتلك الوسيط هذه الفتحات الداخلية، لذلك ستفشل الدوالّ المضمنة.
</p>

<p>
	إليك مثالًا يوضح الأمر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_63" style="">
<span class="pln">let map </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Map</span><span class="pun">();</span><span class="pln">

let proxy </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">map</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{});</span><span class="pln">

proxy</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="str">'test'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Error</span></pre>

<p>
	داخليًا ، تخزن <code>Map</code> جميع البيانات في الفتحة الداخلية <code>[[MapData]]</code>. ولكن الوسيط ليس لديه مثل هذه الفتحة. تحاول <a href="https://tc39.es/ecma262/#sec-map.prototype.set" rel="external nofollow">الدالّة المضمنة <code>Map.prototype.set</code></a> الوصول إلى الخاصية الداخلية <code>this.[[MapData]]‎</code>، ولكن لأن <code>this=proxy</code>، لا يمكن العثور عليه في الوسيط <code>proxy</code> مما سيؤدي لفشل العملية.
</p>

<p>
	لحسن الحظ، هناك طريقة لإصلاحها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_65" style="">
<span class="pln">let map </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Map</span><span class="pun">();</span><span class="pln">

let proxy </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">map</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let value </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Reflect</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(...</span><span class="pln">arguments</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">typeof</span><span class="pln"> value </span><span class="pun">==</span><span class="pln"> </span><span class="str">'function'</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> value</span><span class="pun">.</span><span class="pln">bind</span><span class="pun">(</span><span class="pln">target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

proxy</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="str">'test'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">proxy</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'test'</span><span class="pun">));</span><span class="pln"> </span><span class="com">// 1 (works!)</span></pre>

<p>
	الآن تعمل وفق المطلوب، لأن اعتراض <code>get</code> يربط خاصيات الدوالّ، مثل <code>map.set</code>، بالكائن الهدف (<code>map</code>) نفسه.
</p>

<p>
	بخلاف المثال السابق، فإن قيمة <code>this</code> داخل<code>proxy.set (...)‎</code> لن تكون <code>proxy</code>، وإنما <code>map</code> الأصلية. لذا عند التطبيق الداخلي لـ <code>set</code> سيحاول الوصول إلى الفتحة الداخلية هكذا <code>this.[[MapData]]‎</code>، ولحسن الحظ سينجح.
</p>

<p>
	<strong>ملاحظة</strong>: المصفوفة العادية <code>Array</code> ليس لها فتحات داخلية
</p>

<p>
	استثناء ملحوظ: لا تستخدم المصفوفات المضمنة <code>Array</code> الفتحات الداخلية. هذا لأسباب تاريخية، إذ إنها ظهرت منذ وقت طويل.
</p>

<p>
	لذلك لا توجد مشكلة عند تغليف المصفوفة إلى باستخدام الوسيط.
</p>

<h3>
	الخاصيات الخاصة
</h3>

<p>
	يحدث شيء مشابه للأمر مع خاصيات الصنف الخاصة.
</p>

<p>
	فمثلًا، يمكن للدالّة <code>getName()‎</code> الوصول إلى الخاصية الخاصًة <code>‎#name</code> بدون استخدام الوسيط، ولكن بعد تغليفنا للكائن باستخدام الوسيط ستتوقف إمكانية وصول الدالّة السابقة للخاصّية الخاصّة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_67" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">User</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">#</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Guest"</span><span class="pun">;</span><span class="pln">

  getName</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.#</span><span class="pln">name</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let user </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">User</span><span class="pun">();</span><span class="pln">

user </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{});</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">getName</span><span class="pun">());</span><span class="pln"> </span><span class="com">// Error</span></pre>

<p>
	وذلك بسبب أن التنفيذ الفعلي للخاصّيات الخاصّة يكون باستخدام الفتحات داخلية. ولا تستخدم لغة جافاسكربت الدوالّ <code>[[Get]]/[[Set]]</code> للوصول إليها.
</p>

<p>
	في استدعاء <code>getName()‎</code>، تكون قيمة <code>this</code> هي كائن <code>user</code> المغلّف بالوسيط، ولا يحتوي -هذا الكائن- على فتحة داخلية مع هذه الخاصّيات الخاصّة.
</p>

<p>
	وللمرة الثانية، يكون ربط الدالّة بالكائن من سيحل الأمر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_69" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">User</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">#</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Guest"</span><span class="pun">;</span><span class="pln">

  getName</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.#</span><span class="pln">name</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let user </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">User</span><span class="pun">();</span><span class="pln">

user </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let value </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Reflect</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(...</span><span class="pln">arguments</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">typeof</span><span class="pln"> value </span><span class="pun">==</span><span class="pln"> </span><span class="str">'function'</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> value</span><span class="pun">.</span><span class="pln">bind</span><span class="pun">(</span><span class="pln">target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">getName</span><span class="pun">());</span><span class="pln"> </span><span class="com">// Guest</span></pre>

<p>
	ومع ذلك، فإن لهذا الحل بعض العيوب، كما وضحنا سابقًا: سيعرض هذا الحل الكائن الأصلي لبعض الدوالّ، مما سيُسمح بتمريره لدوالٍ أكثر وبذلك كسر الدوالّ الأخرى المتعلقة بالوسيط.
</p>

<h3>
	Proxy! = target
</h3>

<p>
	إن كلًا من الوسيط والكائن الأصلي مختلفان. وهذا أمر طبيعي، أليس كذلك؟
</p>

<p>
	لذلك إذا استخدمنا الكائن الأصلي كخاصية في المجموعة <code>Set</code>، ثم غلفناه بالوسيط، فعندئذ لن نتمكن من العثور على الوسيط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_71" style="">
<span class="pln">let allUsers </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Set</span><span class="pun">();</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">User</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
    allUsers</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let user </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">User</span><span class="pun">(</span><span class="str">"John"</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">allUsers</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="pln">user</span><span class="pun">));</span><span class="pln"> </span><span class="com">// true</span><span class="pln">

user </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">user</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{});</span><span class="pln">
</span><span class="com">// لاحظ</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">allUsers</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="pln">user</span><span class="pun">));</span><span class="pln"> </span><span class="com">// false</span></pre>

<p>
	كما رأينا، بعد التغليف باستخدام الوسيط لن نتمكن من العثور على كائن <code>user</code> في المجموعة <code>allUsers</code>، لأن الوسيط هو كائن مختلف عن الكائن الأصلي.
</p>

<p>
	لا يمكن للوسطاء <code>proxies</code> اعتراض اختبار المساواة الصارم <code>===</code>.
</p>

<p>
	يمكنها اعترلض العديد من العمليات الأخرى، مثل: <code>new</code> (مع <code>build</code>) و<code>in</code> (مع <code>has</code>) و<code>delete</code> (مع <code>deleteProperty</code>) وما إلى ذلك.
</p>

<p>
	ولكن لا توجد طريقة لاعتراض اختبار المساواة الصارم للكائنات. الكائن يساوي نفسه تمامًا ولا يساوي أيّ كائنٍ آخر.
</p>

<p>
	لذا فإن جميع العمليات والأصناف المضمّنة في اللغة التي توازن بين الكائنات من أجل المساواة ستميز الفرق بين الكائن الأصلي والوسيط. ولا يوجد من يحل محله لإصلاح الأمر.
</p>

<h2>
	الوسيط القابل للتعطيل
</h2>

<p>
	وسيط <em>revocable</em> هو وسيط يمكن تعطيله.
</p>

<p>
	لنفترض أن لدينا موردًا ونود منع الوصول إليه في لحظةٍ ما.
</p>

<p>
	أحد الأشياء التي يمكننا فعلها هو تغليف هذا المورد بالوسيط القابل للتعطيل، بدون أي اعتراضات. بهذه الحالة سيُعيد الوسيط توجيه العمليات للكائن، ويمكننا تعطيله بأي لحظة نريدها.
</p>

<p>
	وصياغته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_73" style="">
<span class="pln">let </span><span class="pun">{</span><span class="pln">proxy</span><span class="pun">,</span><span class="pln"> revoke</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">.</span><span class="pln">revocable</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> handler</span><span class="pun">)</span></pre>

<p>
	الاستدعاء من خلال <code>proxy</code> سيُعيد الكائن والاستدعاء من خلال <code>revoke</code> سيعطل إمكانية الوصول إليه.
</p>

<p>
	إليك المثال لتوضيح الأمر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_75" style="">
<span class="pln">let object </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  data</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Valuable data"</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

let </span><span class="pun">{</span><span class="pln">proxy</span><span class="pun">,</span><span class="pln"> revoke</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">.</span><span class="pln">revocable</span><span class="pun">(</span><span class="pln">object</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{});</span><span class="pln">

</span><span class="com">// مرر الوسيط لمكان آخر بدل الكائن</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">proxy</span><span class="pun">.</span><span class="pln">data</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Valuable data</span><span class="pln">

</span><span class="com">// لاحقا نستدعي التابع</span><span class="pln">
revoke</span><span class="pun">();</span><span class="pln">

</span><span class="com">// ‫لن يعمل الوسيط الآن (لأنه معطلّ)</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">proxy</span><span class="pun">.</span><span class="pln">data</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Error</span></pre>

<p>
	يؤدي استدعاء <code>revoke()‎</code> لإزالة جميع المراجع الداخلية للكائن الهدف من الوسيط، وبذلك لم يعد متصل بأي شيء بعد الآن. كما يمكننا بعد ذلك كنس المخلفات من الذاكرة بإزالة الكائن الهدف.
</p>

<p>
	يمكننا أيضًا تخزين <code>revoke</code> في <code>WeakMap</code>، حتى نتمكن من العثور عليه بسهولة من خلال كائن الوسيط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_77" style="">
<span class="pln">let revokes </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">WeakMap</span><span class="pun">();</span><span class="pln">

let object </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  data</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Valuable data"</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

let </span><span class="pun">{</span><span class="pln">proxy</span><span class="pun">,</span><span class="pln"> revoke</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">.</span><span class="pln">revocable</span><span class="pun">(</span><span class="pln">object</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{});</span><span class="pln">

revokes</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">proxy</span><span class="pun">,</span><span class="pln"> revoke</span><span class="pun">);</span><span class="pln">

</span><span class="com">// ..لاحقًا في الشيفرة..</span><span class="pln">
revoke </span><span class="pun">=</span><span class="pln"> revokes</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">proxy</span><span class="pun">);</span><span class="pln">
revoke</span><span class="pun">();</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">proxy</span><span class="pun">.</span><span class="pln">data</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Error (revoked)</span></pre>

<p>
	تفيدنا هذه الطريقة بأنه ليس علينا بعد الآن حمل <code>revoke</code>. وإنما يمكننا الحصول عليها من <code>map</code> من خلال الوسيط <code>proxy</code> عند الحاجة.
</p>

<p>
	نستخدم <code>WeakMap</code> بدلاً من <code>Map</code> هنا لأنه لن يمنع كنس المخلّفات في الذاكرة. إذا أصبح كائن الوسيط "غير قابل للوصول" (مثلًا، في حال لم يعد هناك متغيّر يشير إليه بعد الآن)، فإن <code>WeakMap</code> يسمح بمسحه من الذاكرة مع <code>revoke</code> خاصته والّتي لن نحتاج إليها بعد الآن.
</p>

<h2>
	المصادر
</h2>

<ul>
<li>
		المواصفات القياسية للوسيط : <a href="https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots" rel="external nofollow">Proxy</a>.
	</li>
	<li>
		توثيق الوسيط الرسمي من <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy" rel="external nofollow">مركز مطوري موزيلا MDN</a>.
	</li>
</ul>
<h2>
	خلاصة
</h2>

<p>
	الوسيط <code>Proxy</code> عبارة عن غلاف حول كائن، يُعيد توجيه العمليات عليه إلى الكائن، ويحبس بعضها بشكل اختياري.
</p>

<p>
	يمكنه تغليف أي نوع من الكائنات، بما في ذلك الأصناف والدوالّ.
</p>

<p>
	صياغته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_79" style="">
<span class="pln">let proxy </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">/* traps */</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	… ثم يجب علينا استخدام "الوسيط" في كل مكان بدلاً من كائن "الهدف". لا يمتلك الوكيل خاصيات أو توابع. يعترض عملية ما إذا زُودَ بالاعتراض المناسب، وإلا سيعيد توجيهها إلى كائن الهدف <code>target</code>.
</p>

<p>
	يمكننا اعتراض:
</p>

<ul>
<li>
		قراءة (<code>get</code>) وكتابة (<code>set</code>) وحذف (<code>deleteProperty</code>) خاصية (حتى الخاصية غير موجودة).
	</li>
	<li>
		استدعاء دالّة ما (الاعتراض <code>apply</code>).
	</li>
	<li>
		المعامل <code>new</code> (الاعتراض <code>construct</code>).
	</li>
	<li>
		العديد من العمليات الأخرى (القائمة الكاملة في بداية المقال وفي <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy" rel="external nofollow">التوثيق الرسمي</a>).
	</li>
</ul>
<p>
	مما سيسمح لنا بإنشاء خاصيات ودوالّ "افتراضية"، وتطبيق قيم افتراضية، وكائنات المراقبة، وزخرفة الدوالّ، وأكثر من ذلك بكثير.
</p>

<p>
	يمكننا أيضًا تغليف كائن ما عدة مرات في وسطاء مختلفة، وزخرفته بمختلف أنواع الوظائف. صُممّت <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect" rel="external nofollow">الواجهة البرمجية</a> للمنعكس لتكمل عمل <a href="https://developer.mozilla.org/ar-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy" rel="external nofollow">الوسيط</a>. بالنسبة لأي اعتراض <code>Proxy</code> ، هناك استدعاء للمنعكس <code>Reflect</code> مقابل له بنفس الوسطاء. يجب علينا استخدامها لإعادة توجيه الاستدعاءات إلى الكائنات المستهدفة.
</p>

<p>
	لدى الوسيط بعض القيود:
</p>

<ul>
<li>
		تحتوي الكائنات المضمّنة في اللغة على "فتحات داخلية"، ولا يمكن الوصول إلى تلك الأشياء بالوسيط. راجع الفقرة المخصصة لها أعلاه.
	</li>
	<li>
		وينطبق الشيء نفسه على خاصيات الصنف الخاصة، إذ تنفيذها داخليًا باستخدام الفتحات. لذا يجب أن تحتوي استدعاءات دوالّ الوسيط على الكائن المستهدف بدل <code>this</code> للوصول إليها.
	</li>
	<li>
		لا يمكن اعتراض اختبارات المساواة الصارمة للكائن <code>===</code>.
	</li>
	<li>
		الأداء: تعتمد المقاييس على المحرك، ولكن عمومًا إن الوصول إلى الخاصية باستخدام وكيل بسيط سيستغرق وقتًا أطول بعض الشيء. عمليًا يهتم بها البعض لعدم حدوث اختناق في الأداء "عنق الزجاجة".
	</li>
</ul>
<h2>
	تمارين
</h2>

<h3>
	خطأ في قراءة الخاصيات غير موجودة في الكائن الأصلي
</h3>

<p>
	عادةً ، تؤدي محاولة قراءة خاصية غير موجودة إلى إعادة النتيجة <code>undefined</code>.
</p>

<p>
	أنشئ وسيطًا يعيد خطأ عند محاولة قراءة خاصية غير موجودة في الكائن الأصلي بدلًا من ذلك.
</p>

<p>
	يمكن أن يساعد ذلك في الكشف عن الأخطاء البرمجية مبكرًا.
</p>

<p>
	اكتب دالّة <code>wrap(target)‎</code> والّتي تأخذ كائنًا <code>target</code> وتعيد وسيطًا والّذي سيُضيف خصائص وظيفية أخرى.
</p>

<p>
	هكذا يجب أن تعمل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_81" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> wrap</span><span class="pun">(</span><span class="pln">target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">/* your code */</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

user </span><span class="pun">=</span><span class="pln"> wrap</span><span class="pun">(</span><span class="pln">user</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="com">// John</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">age</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ReferenceError: Property doesn't exist "age"</span></pre>

<h4>
	الحل
</h4>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_83" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> wrap</span><span class="pun">(</span><span class="pln">target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">prop in target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Reflect</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ReferenceError</span><span class="pun">(`</span><span class="typ">Property</span><span class="pln"> doesn</span><span class="str">'</span><span class="pln">t exist</span><span class="pun">:</span><span class="pln"> </span><span class="str">"${prop}"</span><span class="pun">`)</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

user </span><span class="pun">=</span><span class="pln"> wrap</span><span class="pun">(</span><span class="pln">user</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="com">// John</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">age</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ReferenceError: Property doesn't exist "age"</span></pre>

<h3>
	الوصول إلى الدليل [‎-1] في فهرس المصفوفة
</h3>

<p>
	يمكننا الوصول إلى عناصر المصفوفة باستخدام الفهارس السلبية في بعض لغات البرمجة، محسوبةً بذلك من نهاية المصفوفة.
</p>

<p>
	هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_87" style="">
<span class="pln">let array </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">

array</span><span class="pun">[-</span><span class="lit">1</span><span class="pun">];</span><span class="pln"> </span><span class="com">// 3, آخر عنصر في المصفوفة</span><span class="pln">
array</span><span class="pun">[-</span><span class="lit">2</span><span class="pun">];</span><span class="pln"> </span><span class="com">// 2, خطوة للوراء من نهاية المصفوفة </span><span class="pln">
array</span><span class="pun">[-</span><span class="lit">3</span><span class="pun">];</span><span class="pln"> </span><span class="com">// 1, خطوتين للوراء من نهاية المصفوفة</span></pre>

<p>
	بتعبيرٍ آخر ، فإن <code>array[-N]‎</code> هي نفس <code>array[array.length - N]‎</code>.
</p>

<p>
	أنشئ وسيطًا لتنفيذ هذا السلوك.
</p>

<p>
	هكذا يجب أن تعمل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_89" style="">
<span class="pln">let array </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">

array </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">array</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">/* your code */</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> array</span><span class="pun">[-</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 3</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> array</span><span class="pun">[-</span><span class="lit">2</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 2</span><span class="pln">

</span><span class="com">// بقية الخصائص الوظيفية الأخرى يجب أن تبقى كما هي</span></pre>

<h4>
	الحل
</h4>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_91" style="">
<span class="pln">let array </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">

array </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">array</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">prop </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// ‫حتى وإن وصلنا للمصفوفة هكذا arr[1]‎</span><span class="pln">
      </span><span class="com">// ‫إن المتغيّر prop عبارة عن سلسلة نصية لذا نحتاج لتحويله إلى رقم</span><span class="pln">
      prop </span><span class="pun">=</span><span class="pln"> </span><span class="pun">+</span><span class="pln">prop </span><span class="pun">+</span><span class="pln"> target</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Reflect</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> prop</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">


alert</span><span class="pun">(</span><span class="pln">array</span><span class="pun">[-</span><span class="lit">1</span><span class="pun">]);</span><span class="pln"> </span><span class="com">// 3</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">array</span><span class="pun">[-</span><span class="lit">2</span><span class="pun">]);</span><span class="pln"> </span><span class="com">// 2</span></pre>

<h3>
	المراقب
</h3>

<p>
	أنشئ تابع <code>makeObservable(target)‎</code> الّذي تجعل الكائن قابلاً للمراقبة 'من خلال إعادة وسيط.
</p>

<p>
	إليك كيفية العمل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_93" style="">
<span class="kwd">function</span><span class="pln"> makeObservable</span><span class="pun">(</span><span class="pln">target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">/* your code */</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
user </span><span class="pun">=</span><span class="pln"> makeObservable</span><span class="pun">(</span><span class="pln">user</span><span class="pun">);</span><span class="pln">

user</span><span class="pun">.</span><span class="pln">observe</span><span class="pun">((</span><span class="pln">key</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="pln">SET $</span><span class="pun">{</span><span class="pln">key</span><span class="pun">}=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">value</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

user</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// alerts: SET name=John</span></pre>

<p>
	وبعبارة أخرى، فإن الكائن الّذي سيعاد من خلال <code>makeObservable</code> يشبه تمامًا الكائن الأصلي، ولكنه يحتوي أيضًا على الطريقة <code>observe(handler)‎</code> الّتي ستضبط تابع <code>المُعالج</code> ليستدعى عند أي تغيير في الخاصية.
</p>

<p>
	عندما تتغير خاصية ما، يستدعى <code>handler(key, value)‎</code> مع اسم وقيمة الخاصية.
</p>

<p>
	ملاحظة. في هذا التمرين، يرجى الاهتمام بضبط الخاصيات فقط. يمكن تنفيذ عمليات أخرى بطريقة مماثلة.
</p>

<h4>
	الحل
</h4>

<p>
	يتكون الحل من جزئين:
</p>

<ol>
<li>
		عندما يستدعى <code>‎.observe(handler)‎</code>، نحتاج إلى حفظ المعالج في مكان ما، حتى نتمكن من الاتصال به لاحقًا. يمكننا تخزين المعالجات في الكائن مباشرة، باستخدام الرمز الخاص بنا كمفتاح خاصية.
	</li>
	<li>
		سنحتاج لوسيط مع الاعتراض <code>set</code> لاستدعاء المعالجات عند حدوث أي تغيير.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7123_95" style="">
<span class="pln">let handlers </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Symbol</span><span class="pun">(</span><span class="str">'handlers'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> makeObservable</span><span class="pun">(</span><span class="pln">target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// 1. هيئ مخزّن المعالجات</span><span class="pln">
  target</span><span class="pun">[</span><span class="pln">handlers</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">

  </span><span class="com">// احتفظ بتوابع المعالج في مصفوفة للاستدعاءات اللاحقة</span><span class="pln">
  target</span><span class="pun">.</span><span class="pln">observe </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">handler</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">[</span><span class="pln">handlers</span><span class="pun">].</span><span class="pln">push</span><span class="pun">(</span><span class="pln">handler</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

  </span><span class="com">// ‫2. أنشئ وسيط ليعالج التغييرات</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Proxy</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">set</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> property</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      let success </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Reflect</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(...</span><span class="pln">arguments</span><span class="pun">);</span><span class="pln"> </span><span class="com">// وجه العملية إلى الكائن</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">success</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// إن حدث خطأ ما في ضبط الخاصية</span><span class="pln">
        </span><span class="com">// استدعي جميع المعالجات</span><span class="pln">
        target</span><span class="pun">[</span><span class="pln">handlers</span><span class="pun">].</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">handler </span><span class="pun">=&gt;</span><span class="pln"> handler</span><span class="pun">(</span><span class="pln">property</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">));</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> success</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">

user </span><span class="pun">=</span><span class="pln"> makeObservable</span><span class="pun">(</span><span class="pln">user</span><span class="pun">);</span><span class="pln">

user</span><span class="pun">.</span><span class="pln">observe</span><span class="pun">((</span><span class="pln">key</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="pln">SET $</span><span class="pun">{</span><span class="pln">key</span><span class="pun">}=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">value</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

user</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">;</span></pre>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/proxy" rel="external nofollow">Proxy and Reflect</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">929</guid><pubDate>Wed, 24 Jun 2020 11:20:04 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x64A;&#x631;&#x627;&#x62F; &#x627;&#x644;&#x648;&#x62D;&#x62F;&#x627;&#x62A; &#x62F;&#x64A;&#x646;&#x627;&#x645;&#x64A;&#x643;&#x64A;&#x64B;&#x627; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D9%8A%D8%B1%D8%A7%D8%AF-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%AF%D9%8A%D9%86%D8%A7%D9%85%D9%8A%D9%83%D9%8A%D9%8B%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r928/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/82.jpg.0ab5799117fa34f51e198828d57f03a7.jpg" /></p>

<p>
	إن طريقة الاستيراد والتصدير التي تحدثنا عنها في الفصل السابق، <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B5%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D9%88%D8%A7%D8%B3%D8%AA%D9%8A%D8%B1%D8%A7%D8%AF%D9%87%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r927/" rel="">تصدير الوحدات واستيرادها</a> تدعى بالطريقة "الثابتة". إذ أنّ صياغتها بسيطة وصارمة للغاية.
</p>

<p>
	دعنا في البداية نوضح بعض الأمور، أولًا، لا يمكننا إنشاء أي وسطاء للتعليمة <code>import</code> إنشاءً ديناميكيًا.
</p>

<p>
	إذ يجب أن يكون مسار الوِحدة سلسلة أولية (primitive)، ولا يجب أن تكون استدعاءً لدالة معينة. فهذا لن ينجح:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5119_7" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">...</span><span class="pln"> from getModuleName</span><span class="pun">();</span><span class="pln"> </span><span class="com">// خطأ، مسموح استخدام السلاسل فقط</span></pre>

<p>
	ثانيًا، لا يمكننا استخدام الاستيراد المشروط (في حال حدوث شرط معين استورد مكتبة) أو الاستيراد أثناء التشغيل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5119_9" style="">
<span class="kwd">if</span><span class="pun">(...)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">...;</span><span class="pln"> </span><span class="com">// ‫خطأ غير مسموح بذلك!</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">...;</span><span class="pln">  </span><span class="com">// ‫خطأ، لا يمكننا وضع تعليمة import في أي كتلة</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وذلك لأن تعليمتي <code>import</code>/<code>export</code> تهدفان لتوفير العمود الفقري لبنية الشيفرة. وهذا أمر جيد، إذ يمكننا تحليل بنية الشيفرة، وتجميع الوحدات وتحزيمها في ملف واحد من خلال أدوات خاصة، ويمكننا أيضًا إزالة عمليات التصدير غير المستخدمة (هزّ الشجرة -بهدف سقوط الأوراق اليابسة). هذا ممكن فقط لأن هيكلية التعليمتين <code>import</code>/<code>export</code> بسيطة وثابتة.
</p>

<p>
	ولكن كيف يمكننا استيراد وِحدة استيرادً ديناميكيًا بحسب الطلب؟
</p>

<h2>
	تعبير الاستيراد
</h2>

<p>
	يُحمّل التعبير <code>import (module)‎</code> الوِحدة ويُرجع وعدًا، والّذي يُستبدل بكائن الوِحدة، ويحتوي هذا الأخير على كافة عمليات التصدير الخاصة بالكائن. ويُستدعى من أي مكان في الشيفرة البرمجية.
</p>

<p>
	ونستطيع استخدامه ديناميكيًا في أي مكان من الشيفرة البرمجية، فمثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5119_11" style="">
<span class="pln">let modulePath </span><span class="pun">=</span><span class="pln"> prompt</span><span class="pun">(</span><span class="str">"Which module to load?"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">import</span><span class="pun">(</span><span class="pln">modulePath</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">obj </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">module object</span><span class="pun">&gt;)</span><span class="pln">
  </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">loading error</span><span class="pun">,</span><span class="pln"> e</span><span class="pun">.</span><span class="pln">g</span><span class="pun">.</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> no such module</span><span class="pun">&gt;)</span></pre>

<p>
	أو يمكننا استخدام <code>let module = await import(modulePath)‎</code> إن كنا بداخل دالّة غير متزامنة.
</p>

<p>
	فمثلًا، ليكن لدينا الوِحدة التالية <code>say.js</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5119_13" style="">
<span class="com">// ? say.js</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> hi</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Hello</span><span class="pun">`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> bye</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Bye</span><span class="pun">`);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	… ثم يكون الاستيراد الديناميكي هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5119_15" style="">
<span class="pln">let </span><span class="pun">{</span><span class="pln">hi</span><span class="pun">,</span><span class="pln"> bye</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> await </span><span class="kwd">import</span><span class="pun">(</span><span class="str">'./say.js'</span><span class="pun">);</span><span class="pln">

hi</span><span class="pun">();</span><span class="pln">
bye</span><span class="pun">();</span></pre>

<p>
	أو إذا كان <code>say.js</code> يحتوي على التصدير المبدئي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5119_17" style="">
<span class="com">// ? say.js</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">"Module loaded (export default)!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	… بعد ذلك، من أجل الوصول إليه، يمكننا استخدام الخاصية <code>default</code> لكائن الوِحدة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5119_21" style="">
<span class="pln">let obj </span><span class="pun">=</span><span class="pln"> await </span><span class="kwd">import</span><span class="pun">(</span><span class="str">'./say.js'</span><span class="pun">);</span><span class="pln">
let say </span><span class="pun">=</span><span class="pln"> obj</span><span class="pun">.</span><span class="kwd">default</span><span class="pun">;</span><span class="pln">
</span><span class="com">// ‫أو بسطرٍ واحد هكذا:‎</span><span class="pln">
</span><span class="com">// let {default: say} = await import('./say.js');</span><span class="pln">

say</span><span class="pun">();</span></pre>

<p>
	إليك المثال الكامل: الملف <code>say.js</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5119_23" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> hi</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Hello</span><span class="pun">`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> bye</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Bye</span><span class="pun">`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">"Module loaded (export default)!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الملف <code>index.html</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5119_25" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;script&gt;</span><span class="pln">
  async </span><span class="kwd">function</span><span class="pln"> load</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let say </span><span class="pun">=</span><span class="pln"> await </span><span class="kwd">import</span><span class="pun">(</span><span class="str">'./say.js'</span><span class="pun">);</span><span class="pln">
    say</span><span class="pun">.</span><span class="pln">hi</span><span class="pun">();</span><span class="pln"> </span><span class="com">// Hello!</span><span class="pln">
    say</span><span class="pun">.</span><span class="pln">bye</span><span class="pun">();</span><span class="pln"> </span><span class="com">// Bye!</span><span class="pln">
    say</span><span class="pun">.</span><span class="kwd">default</span><span class="pun">();</span><span class="pln"> </span><span class="com">// Module loaded (export default)!</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">onclick</span><span class="pun">=</span><span class="atv">"</span><span class="pln">load</span><span class="pun">()</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">Click me</span><span class="tag">&lt;/button&gt;</span></pre>

<p>
	وإليك النتائج في هذا <a href="https://javascript.info/article/modules-dynamic-imports/say/" rel="external nofollow">المثال حي</a>.
</p>

<p>
	لاحظ كيف تعمل عمليات الاستيراد الديناميكية في السكربتات العادية، ولا تتطلب <code>script type ="module"‎</code>.
</p>

<p>
	لاحظ أيضًا على الرغم من أن تعليمة <code>import()‎</code> تشبه طريقة استدعاء دالّة، إلا أنها صياغة خاصة ويحدث هذا التشابه فقط لاستخدام الأقواس (على غرار <code>super ()‎</code>).
</p>

<p>
	لذلك لا يمكننا نسخ تعليمة <code>import</code> إلى متغير، أو استخدام <code>call/apply</code> معها. إذ هي ليست دالّة.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/modules-dynamic-imports" rel="external nofollow">Dynamic imports</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">928</guid><pubDate>Wed, 24 Jun 2020 10:14:09 +0000</pubDate></item><item><title>&#x62A;&#x635;&#x62F;&#x64A;&#x631; &#x627;&#x644;&#x648;&#x62D;&#x62F;&#x627;&#x62A; &#x648;&#x627;&#x633;&#x62A;&#x64A;&#x631;&#x627;&#x62F;&#x647;&#x627; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B5%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D9%88%D8%A7%D8%B3%D8%AA%D9%8A%D8%B1%D8%A7%D8%AF%D9%87%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r927/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/81.jpg.f8c3324ba264a64faecd0925e7ea197a.jpg" /></p>

<p>
	لمُوجِّهات (تعليمات) الاستيراد والتصدير أكثر من صياغة برمجية واحدة رأينا في الفصل السابق، <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r926/" rel="">مقدمة إلى الوحدات</a> استعمالًا بسيطًا له، فهيًا نرى بقية الاستعمالات.
</p>

<h2>
	التصدير قبل التصريح
</h2>

<p>
	يمكننا أن نقول لأيّ تصريح بأنّه مُصدّر بوضع عبارة <code>export</code> قبله، كان التصريح عن متغيّر أو عن دالة أو عن صنف.
</p>

<p>
	فمثلًا، التصديرات هنا كلّها صحيحة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_7" style="">
<span class="com">// تصدير مصفوفة</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> let months </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'Jan'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Feb'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mar'</span><span class="pun">,</span><span class="str">'Apr'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Aug'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Sep'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Oct'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Nov'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Dec'</span><span class="pun">];</span><span class="pln">

</span><span class="com">// تصدير ثابت</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> MODULES_BECAME_STANDARD_YEAR </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2015</span><span class="pun">;</span><span class="pln">

</span><span class="com">// تصدير صنف</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">User</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<strong>ملاحظة</strong>: لا يوجد فواصل منقوطة بعد تعليمة التصدير للأصناف أو الدوالّ لاحظ أن تعليمة <code>export</code> قبل الصنف أو الدالة لا يجعلها <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%B3%D9%87%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r782/" rel="">تعابير الدوالّ</a>. ولو أنه يصُدرها، لكنه لا يزال تعريفًا للدالّة أو الصنف.
</p>

<p>
	لا توصي معظم الأدلة التعليمية بوضع فاصلة منقوطة بعد تعريف الدوال والأصناف.
</p>

<p>
	لهذا السبب لا داعي للفاصلة المنقوطة في نهاية التعليمة <code>export class</code> والتعليمة <code>export function</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_9" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> sayHi</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">user</span><span class="pun">}!`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="com">// لاحظ لا يوجد فاصلة منقوطة في نهاية التعريف</span></pre>

<h2>
	التصدير بعيدًا عن التصريح
</h2>

<p>
	كما يمكننا وضع عبارة <code>export</code> لوحدها.
</p>

<p>
	هنا نصرّح أولًا عن الدالتين وبعدها نُصدّرهما:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_12" style="">
<span class="com">// ? say.js</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> sayHi</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">user</span><span class="pun">}!`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> sayBye</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Bye</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">user</span><span class="pun">}!`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="kwd">export</span><span class="pln"> </span><span class="pun">{</span><span class="pln">sayHi</span><span class="pun">,</span><span class="pln"> sayBye</span><span class="pun">};</span><span class="pln"> </span><span class="com">// تصدير قائمة من المتغيرات</span></pre>

<p>
	أو… يمكننا تقنيًا وضع <code>export</code> أعلى الدوال أيضًا.
</p>

<h2>
	عبارة استيراد كل شيء
</h2>

<p>
	عادةً نضع قائمة بما نريد استيراده في أقواس معقوفة <code>import {...}‎</code>، هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_14" style="">
<span class="com">// ? main.js</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">sayHi</span><span class="pun">,</span><span class="pln"> sayBye</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./say.js'</span><span class="pun">;</span><span class="pln">


sayHi</span><span class="pun">(</span><span class="str">'John'</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Hello, John!</span><span class="pln">
sayBye</span><span class="pun">(</span><span class="str">'John'</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Bye, John!</span></pre>

<p>
	ولكن لو أردنا استيراد وحدات كثيرة، فيمكننا استيراد كلّ شيء كائنًا واحدًا باستعمال <code>import * as &lt;obj&gt;‎</code> هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_16" style="">
<span class="com">// ? main.js</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> as say from </span><span class="str">'./say.js'</span><span class="pun">;</span><span class="pln">


say</span><span class="pun">.</span><span class="pln">sayHi</span><span class="pun">(</span><span class="str">'John'</span><span class="pun">);</span><span class="pln">
say</span><span class="pun">.</span><span class="pln">sayBye</span><span class="pun">(</span><span class="str">'John'</span><span class="pun">);</span></pre>

<p>
	يقول المرء من النظرة الأولى ”استيراد كلّ شيء فكرة جميلة جدًا، وكتابة الشيفرة سيكون أسرع. أساسًا لمَ نقول جهارةً ما نريد استيراده؟“
</p>

<p>
	ذلك… لأسباب وجيهة.
</p>

<ol>
<li>
		<p>
			أدوات البناء الحديثة (مثل: <a href="http://webpack.github.io/" rel="external nofollow">webpack</a> وغيرها)
		</p>

		<p>
			لنقل مثلًا بأنّا أضفنا مكتبة خارجية اسمها <code>say.js</code> إلى مشروعنا، وفيها دوالّ عديدة:
		</p>

		<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_18" style="">
<span class="com">// ? say.js</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> sayHi</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">...</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> sayBye</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">...</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> becomeSilent</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">...</span><span class="pln"> </span><span class="pun">}</span></pre>

		<p>
			هكذا نستعمل واحدة فقط من دوالّ <code>say.js</code> في مشروعنا:
		</p>

		<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_20" style="">
<span class="com">// ? main.js</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">sayHi</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./say.js'</span><span class="pun">;</span></pre>

		<p>
			…حينها تأتي أداة التحسين وترى ذلك، فتُزيل الدوال الأخرى من الشيفرة … بذلك يصغُر حجم الملف المبني. هذا ما نسميه هز الشجر (لتَسقطَ الأوراق اليابسة فقط).
		</p>
	</li>
	<li>
		<p>
			لو وضّحنا بالضبط ما نريد استيراده فيمكننا كتابته باسم أقصر: <code>sayHi()‎</code> بدل <code>say.sayHi()‎</code>.
		</p>
	</li>
	<li>
		<p>
			بكتابة قائمة الاستيراد جهارةً نستطيع أن نفهم بنية الشيفرة دون الخوض في التفاصيل (أي نعرف ما نستعمل من وحدات، وأين نستعملها). هذا يسهّل دعم الشيفرة وإعادة كتابتها لو تطلّب الأمر.
		</p>
	</li>
</ol>
<h2>
	استيراد كذا بالاسم كذا <code>as</code>
</h2>

<p>
	يمكننا كذلك استعمال <code>as</code> لاستيراد ما نريد بأسماء مختلفة.
</p>

<p>
	فمثلًا يمكننا استيراد الدالة <code>sayHi</code> في المتغير المحلي <code>hi</code> لنختصر الكلام، واستيراد <code>sayBye</code> على أنّها <code>bye</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_22" style="">
<span class="com">// ? main.js</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">sayHi as hi</span><span class="pun">,</span><span class="pln"> sayBye as bye</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./say.js'</span><span class="pun">;</span><span class="pln">

hi</span><span class="pun">(</span><span class="str">'John'</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Hello, John!</span><span class="pln">
bye</span><span class="pun">(</span><span class="str">'John'</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Bye, John!</span></pre>

<h2>
	تصدير كذا بالاسم كذا <code>as</code>
</h2>

<p>
	نفس صياغة الاستيراد موجودة أيضًا للتصدير <code>export</code>.
</p>

<p>
	فلنصدّر الدوال على أنّها <code>hi</code> و<code>bye</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_24" style="">
<span class="com">// ? say.js</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="pun">{</span><span class="pln">sayHi as hi</span><span class="pun">,</span><span class="pln"> sayBye as bye</span><span class="pun">};</span></pre>

<p>
	الآن صارت <code>hi</code> و<code>bye</code> هي الأسماء ”الرسمية“ للشيفرات الخارجية وستُستعمل عند الاستيراد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_26" style="">
<span class="com">// ? main.js</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> as say from </span><span class="str">'./say.js'</span><span class="pun">;</span><span class="pln">

</span><span class="com">// لاحِظ الفرق</span><span class="pln">
say</span><span class="pun">.</span><span class="pln">hi</span><span class="pun">(</span><span class="str">'John'</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Hello, John!</span><span class="pln">
say</span><span class="pun">.</span><span class="pln">bye</span><span class="pun">(</span><span class="str">'John'</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Bye, John!</span></pre>

<h2>
	التصدير المبدئي
</h2>

<p>
	في الواقع العملي، ثمّة نوعين رئيسين من الوحدات.
</p>

<ol>
<li>
		تلك التي تحتوي مكتبة (أي مجموعة من الدوال) مثل وحدة <code>say.js</code> أعلاه.
	</li>
	<li>
		وتلك التي تصرّح عن كيانٍ واحد مثل وحدة <code>user.js</code> التي تُصدّر <code>class User</code> فقط.
	</li>
</ol>
<p>
	عادةً ما يُحبّذ استعمال الطريقة الثانية كي يكون لكلّ ”شيء“ وحدةً خاصة به.
</p>

<p>
	ولكن هذا بطبيعة الحال يطلب ملفات كثيرة إذ يطلب كلّ شيء وحدةً تخصّه باسمه، ولكنّ هذه ليست بمشكلة، أبدًا. بل على العكس هكذا يصير التنقل في الشيفرة أسهل (لو كانت تسمية الملفات مرضية ومرتّبة في مجلدات).
</p>

<p>
	توفر الوِحدات طريقة لصياغة عبارة <code>export default</code> (التصدير المبدئي) لجعل "سطر تصدير واحد لكلّ وِحدة" تبدو أفضل.
</p>

<p>
	ضَع <code>export default</code> قبل أيّ كيان لتصديره:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_28" style="">
<span class="com">// ? user.js</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">User</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// ‫نُضيف ”default“ فقط</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لكلّ ملف سطر تصدير <code>export default</code> واحد لا أكثر.
</p>

<p>
	وبعدها… نستورد الكيان بدون الأقواس المعقوفة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_30" style="">
<span class="com">// ? main.js</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">User</span><span class="pln"> from </span><span class="str">'./user.js'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// ‫لا نضع {User}، بل User</span><span class="pln">

</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">User</span><span class="pun">(</span><span class="str">'John'</span><span class="pun">);</span></pre>

<p>
	أسطر الاستيراد التي لا تحتوي الأقواس المعقوفة أجمل من تلك التي تحتويها. يشيع خطأ نسيان تلك الأقواس حين يبدأ المطورون باستعمال الوِحدات. لذا تذكّر دائمًا، يطلب سطر الاستيراد <code>import</code> أقواس معقوفة للكيانات المُصدّرة والتي لها أسماء، ولا يطلبها لتلك المبدئية.
</p>

<table>
<thead><tr>
<th>
				التصدير الذي له اسم
			</th>
			<th>
				التصدير المبدئي
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>export class User {...}</code>‎
			</td>
			<td>
				<code>export default class User {...}‎</code>
			</td>
		</tr>
<tr>
<td>
				<code>import {User} from ...</code>‎
			</td>
			<td>
				<code>import User from ...</code>‎
			</td>
		</tr>
</tbody>
</table>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
}

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<p>
	يمكننا نظريًا وضع النوعين من التصدير معًا في نفس الوحدة (الذي له اسم والمبدئي)، ولكن عمليًا لا يخلط الناس عادةً بينها، بل للوِحدة إمّا تصديرات لها أسماء، أو التصدير المبدئي.
</p>

<p>
	ولأنّه لا يمكن أن يكون لكلّ ملف إلا تصديرًا مبدئيًا واحدًا، فيمكن للكيان الذي صُدّر ألّا يحمل أيّ اسم.
</p>

<p>
	فمثلًا التصديرات أسفله كلّها صحيحة مئة في المئة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_32" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// لا اسم للصنف</span><span class="pln">
  constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">...</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_36" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// لا اسم للدالة</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">user</span><span class="pun">}!`);</span><span class="pln">
</span><span class="pun">}</span></pre>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_34" style="">
<span class="com">// نُصدّر قيمةً واحدة دون صنع متغيّر</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="pun">[</span><span class="str">'Jan'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Feb'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Mar'</span><span class="pun">,</span><span class="str">'Apr'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Aug'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Sep'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Oct'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Nov'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Dec'</span><span class="pun">];</span></pre>

<p>
	لا مشكلة بتاتًا بعدم كتابة الاسم إذ لا نرى <code>export default</code> إلّا مرّة في الملف، بهذا تعرف تمامًا أسطر <code>import</code> (بدون استعمال الأقواس المعقوفة) ما عليها استيراده.
</p>

<p>
	ولكن دون <code>default</code> فهذا التصدير سيُعطينا خطأً:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_38" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// Error! (non-default export needs a name)</span><span class="pln">
  constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	الاسم المبدئي
</h3>

<p>
	تُستعمل في حالات معيّنة الكلمة المفتاحية <code>default</code> للإشارة إلى التصدير المبدئي.
</p>

<p>
	فمثلًا لتصدير الدالة بنحوٍ منفصل عن تعريفها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_40" style="">
<span class="kwd">function</span><span class="pln"> sayHi</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">user</span><span class="pun">}!`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// ‫كما لو أضفنا ”export default“ قبل الدالة</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="pun">{</span><span class="pln">sayHi as </span><span class="kwd">default</span><span class="pun">};</span></pre>

<p>
	أو لنقل بأنّ الوحدة <code>user.js</code> تُصدّر شيئًا واحدًا ”مبدئيًا“ وأخرى لها أسماء (نادرًا ما يحدث، ولكنّه يحدث):
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_42" style="">
<span class="com">// ? user.js</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">User</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> sayHi</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">user</span><span class="pun">}!`);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هكذا نستورد التصدير المبدئي مع ذلك الذي لديه اسم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_44" style="">
<span class="com">// ? main.js</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="kwd">default</span><span class="pln"> as </span><span class="typ">User</span><span class="pun">,</span><span class="pln"> sayHi</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./user.js'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">User</span><span class="pun">(</span><span class="str">'John'</span><span class="pun">);</span></pre>

<p>
	وأخيرًا، حين نستورد كلّ شيء <code>*</code> على أنّه كائن، فستكون خاصية <code>default</code> هي كما التصدير المبدئي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_46" style="">
<span class="com">// ? main.js</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> as user from </span><span class="str">'./user.js'</span><span class="pun">;</span><span class="pln">

let </span><span class="typ">User</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> user</span><span class="pun">.</span><span class="kwd">default</span><span class="pun">;</span><span class="pln"> </span><span class="com">// the default export</span><span class="pln">
</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">User</span><span class="pun">(</span><span class="str">'John'</span><span class="pun">);</span></pre>

<p>
	كلمتين بخصوص سوء التصديرات المبدئية
</p>

<p>
	التصديرات التي لها أسماء تكون صريحة، أي أنّها تقول تمامًا ما الّذي يجب أن نستورده، وبذلك يكون لدينا هذه المعلومات منهم، وهذا شيء جيد.
</p>

<p>
	تُجبرنا التصديرات التي لها أسماء باستعمال الاسم الصحيح كما هو بالضبط لاستيراد الوحدة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_48" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="typ">User</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./user.js'</span><span class="pun">;</span><span class="pln">
</span><span class="com">// ‫ولن تعمل import {MyUser}‎ إذ يجب أن يكون الاسم {User}</span></pre>

<p>
	بينما في حالة التصدير المبدئي نختار نحن الاسم حين نستورد الوِحدة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_50" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">User</span><span class="pln"> from </span><span class="str">'./user.js'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// works</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">MyUser</span><span class="pln"> from </span><span class="str">'./user.js'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// works too</span><span class="pln">
</span><span class="com">// ‫ويمكن أيضًا أن تكون ”استورِد كل شيء“ import Anything... وستعمل بلا أدنى مشكلة</span></pre>

<p>
	هذا قد يؤدّي إلى أن يستعمل أعضاء الفريق أسماء مختلفة لاستيراد الشيء ذاته، وهذا طبعًا ليس بالجيد.
</p>

<p>
	عادةً ولنتجنّب ذلك ونُحافظ على اتساق الشيفرة، نستعمل القاعدة القائلة بأنّ أسماء المتغيرات المُستورَدة يجب أن تُوافق أسماء الملفات، هكذا مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_52" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">User</span><span class="pln"> from </span><span class="str">'./user.js'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">LoginForm</span><span class="pln"> from </span><span class="str">'./loginForm.js'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> func from </span><span class="str">'/path/to/func.js'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	مع ذلك تنظُر بعض الفِرق لهذا الأمر على أنه عقبة للتصديرات المبدئية فتفضّل استعمال التصديرات التي لها اسم دومًا. فحتّى لو كانت نصدّر شيئًا واحدًا فقط فما زالت تُصدّره باسم دون استعمال <code>default</code>.
</p>

<p>
	كما يسهّل هذا إعادة التصدير (طالِع أسفله).
</p>

<h2>
	إعادة التصدير
</h2>

<p>
	تُتيح لنا صياغة ”إعادة التصدير“ <code>export ... from ...‎</code> استيراد الأشياء وتصديرها مباشرةً (ربما باسم آخر) هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_54" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="pun">{</span><span class="pln">sayHi</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./say.js'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// ‫نُعيد تصدير sayHi</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="pun">{</span><span class="kwd">default</span><span class="pln"> as </span><span class="typ">User</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./user.js'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// نُعيد تصدير المبدئي</span></pre>

<p>
	ولكن فيمَ نستعمل هذا أصلًا؟ لنرى مثالًا عمليًا.
</p>

<p>
	لنقل بأننا نكتب ”حزمة“، أي مجلدًا فيه وحدات كثيرة وأردنا تصدير بعض ميزاتها إلى الخارج (تتيح لنا الأدوات مثل NPM نشر هذه الحزم وتوزيعها)، ونعلم أيضًا أن الكثير من وحداتها ما هي إلّا وحدات مُساعِدة
</p>

<p>
	يمكن أن تكون بنية الملفات هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_56" style="">
<span class="pln">auth</span><span class="pun">/</span><span class="pln">
    index</span><span class="pun">.</span><span class="pln">js  
    user</span><span class="pun">.</span><span class="pln">js
    helpers</span><span class="pun">.</span><span class="pln">js
    tests</span><span class="pun">/</span><span class="pln">
        login</span><span class="pun">.</span><span class="pln">js
    providers</span><span class="pun">/</span><span class="pln">
        github</span><span class="pun">.</span><span class="pln">js
        facebook</span><span class="pun">.</span><span class="pln">js
        </span><span class="pun">...</span></pre>

<p>
	ونريد عرض مزايا الحزمة باستعمال نقطة واحدة (أي الملف الأساسي <code>auth/index.js</code>) لتُستعمل هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_58" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">login</span><span class="pun">,</span><span class="pln"> logout</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'auth/index.js'</span></pre>

<p>
	الفكرة هي عدم السماح للغرباء (أي المطوّرين مستعملي الحزمة) بالتعديل على البنية الداخلية والبحث عن الملفات داخل مجلد الحزمة. نريد تصدير المطلوب فقط في <code>auth/index.js</code> وإخفاء الباقي عن أعين المتطفّلين.
</p>

<p>
	نظرًا لكون الوظيفة الفعلية المصدّرة مبعثرة بين الحزمة، يمكننا استيرادها إلى <code>auth/index.js</code> وتصديرها من هنالك أيضًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_60" style="">
<span class="com">// ? auth/index.js</span><span class="pln">

</span><span class="com">// ‫اِستورد login/logout وصدِرهن مباشرةً</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">login</span><span class="pun">,</span><span class="pln"> logout</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./helpers.js'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="pun">{</span><span class="pln">login</span><span class="pun">,</span><span class="pln"> logout</span><span class="pun">};</span><span class="pln">

</span><span class="com">// ‫استورد الملف المبدئي كـ User وصدره من جديد</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">User</span><span class="pln"> from </span><span class="str">'./user.js'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="pun">{</span><span class="typ">User</span><span class="pun">};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	والآن يمكن لمستخدمي الحزمة الخاصة بنا استيرادها هكذا <code>import {login} from "auth/index.js"‎</code>.
</p>

<p>
	إن الصياغة <code>export ... from ...‎</code> ماهي إلا اختصار للاستيراد والتصدير:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_62" style="">
<span class="com">// ? auth/index.js</span><span class="pln">
</span><span class="com">// ‫اِستورد login/logout وصدِرهن مباشرةً</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="pun">{</span><span class="pln">login</span><span class="pun">,</span><span class="pln"> logout</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./helpers.js'</span><span class="pun">;</span><span class="pln">

</span><span class="com">// ‫استورد الملف المبدئي كـ User وصدره من جديد</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="pun">{</span><span class="kwd">default</span><span class="pln"> as </span><span class="typ">User</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./user.js'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">...</span></pre>

<h3>
	إعادة تصدير التصديرات المبدئية
</h3>

<p>
	يحتاج التصدير المبدئي لمعالجة منفصلة عند إعادة التصدير.
</p>

<p>
	لنفترض أن لدينا <code>user.js</code>، ونود إعادة تصدير الصنف <code>User</code> منه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_64" style="">
<span class="com">// ? user.js</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">User</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<ol>
<li>
		<p>
			لن تعمل التعليمة <code>export User from './user.js'‎</code>. ما الخطأ الذي حدث؟ ولكن هذا الخطأ في صياغة!
		</p>

		<p>
			لإعادة تصدير الملفات المصدرة إفتراضيًا ، علينا كتابة <code>export {default as User}‎</code> ، كما في المثال أعلاه.
		</p>
	</li>
	<li>
		<p>
			تعيد التعليمة <code>export * from './user.js'‎</code> تصدير التصديرات الّتي لها أسماء فقط، ولكنها تتجاهل التصديرات المبدئية.
		</p>

		<p>
			إذا رغبنا في إعادة تصدير التصديرات المبدئية والتي لها أسماء أيضًا، فسنحتاج إلى العبارتين:
		</p>

		<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_66" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> from </span><span class="str">'./user.js'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// لإعادة تصدير التصديرات الّتي لها أسماء</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="pun">{</span><span class="kwd">default</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./user.js'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// لإعادة تصدير التصديرات المبدئية</span></pre>

		<p>
			هذه الغرابة في طريقة إعادة تصدير التصديرات المبدئية هي من أحد الأسباب لجعل بعض المطورين لا يحبونها.
		</p>
	</li>
</ol>
<h2>
	خلاصة
</h2>

<p>
	والآن سنراجع جميع أنواع طرق التصدير <code>export</code> التي تحدثنا عنها في هذا الفصل والفصول السابقة.
</p>

<p>
	تحقق من معلوماتك بقراءتك لهم وتذكر ما تعنيه كلُّ واحدةٍ منهم:
</p>

<ul>
<li>
		قبل التعريف عن صنف / دالّة / ..:
		<ul>
<li>
				<code>export [default] class/function/variable ...‎</code>
			</li>
		</ul>
</li>
	<li>
		تصدير مستقل:
		<ul>
<li>
				<code>export {x [as y], ...}‎</code>.
			</li>
		</ul>
</li>
	<li>
		إعادة التصدير:
		<ul>
<li>
				<code>export {x [as y], ...} from "module"‎</code>
			</li>
			<li>
				<code>export * from "module"‎</code> (لا يُعيد التصدير المبدئي).
			</li>
			<li>
				<code>export {default [as y]} from "module"‎</code> (يعيد التصدير المبدئي).
			</li>
		</ul>
</li>
</ul>
<p>
	استيراد:
</p>

<ul>
<li>
		الصادرات التي لها أسماء من الوِحدة:
		<ul>
<li>
				<code>import {x [as y], ...} from "module"‎</code>
			</li>
		</ul>
</li>
	<li>
		التصدير المبدئي:
		<ul>
<li>
				<code>import x from "module"‎</code>
			</li>
			<li>
				<code>import {default as x} from "module"‎</code>
			</li>
		</ul>
</li>
	<li>
		استيراد كل شيء:
		<ul>
<li>
				<code>import * as obj from "module"‎</code>
			</li>
		</ul>
</li>
	<li>
		استيراد الوحدة (وشغِّل شيفرتها البرمجية)، ولكن لا تُسندها لمتغير:
		<ul>
<li>
				<code>import "module"‎</code>
			</li>
		</ul>
</li>
</ul>
<p>
	لا يهم مكان وضع عبارات (تعليمات) <code>import/export</code> سواءً في أعلى أو أسفل السكربت فلن يغير ذلك أي شيء.
</p>

<p>
	لذا تقنيًا تعدُّ هذه الشيفرة البرمجية لا بأس بها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_68" style="">
<span class="pln">sayHi</span><span class="pun">();</span><span class="pln">

</span><span class="com">// ...</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">sayHi</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./say.js'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// اِستورد في نهاية الملف</span></pre>

<p>
	عمليًا عادة ما تكون تعليمات الاستيراد في بداية الملف فقط لتنسيق أفضل للشيفرة.
</p>

<p>
	لاحظ أن تعليمتي import/export لن يعملا إن كانا في داخل جملة شرطية.
</p>

<p>
	لن يعمل الاستيراد الشرطي مثل هذا المثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6084_70" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">something</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">sayHi</span><span class="pun">}</span><span class="pln"> from </span><span class="str">"./say.js"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// Error: import must be at top level</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	.. ولكن ماذا لو احتجنا حقًا لاستيراد شيء ما بشروط معينة؟ أو في وقتٍ ما؟ مثل: تحميل الوِحدة عند الطلب، عندما تكون هناك حاجة إليها حقًا؟
</p>

<p>
	سنرى الاستيراد الديناميكي في المقالة التالية.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/import-export" rel="external nofollow">Export and Import</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">927</guid><pubDate>Wed, 24 Jun 2020 10:02:17 +0000</pubDate></item><item><title>&#x645;&#x642;&#x62F;&#x645;&#x629; &#x625;&#x644;&#x649; &#x627;&#x644;&#x648;&#x62D;&#x62F;&#x627;&#x62A; Modules &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r926/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/80.jpg.70260eb82513c909f471b4c362f35285.jpg" /></p>

<p>
	سنرى سريعًا بينما تطبيقنا يكبُر حجمًا وتعقيدًا بأنّ علينا تقسيمه إلى ملفات متعدّدة، أو ”وحدات“ (module). عادةً ما تحتوي الوِحدة على صنف أو مكتبة فيها دوالّ.
</p>

<p>
	كانت محرّكات جافاسكربت تعمل لفترة طويلة جدًا دون أيّ صياغة وِحدات على مستوى اللغة، ولم تكن هذه بالمشكلة إذ أنّ السكربتات سابقًا كانت بسيطة وسهلة ولم يكن هناك داعٍ فعلي للوِحدات.
</p>

<p>
	ولكن كالعادة صارت السكربتات هذه أكثر تعقيدًا وأكبر، فكان على المجتمع اختراع طرائق مختلفة لتنظيم الشيفرات في وحدات (أو مكتبات خاصّة تُحمّل تلك الوِحدات حين الطلب).
</p>

<p>
	مثال:
</p>

<ul>
<li>
		<a href="https://en.wikipedia.org/wiki/Asynchronous_module_definition" rel="external nofollow">AMD</a>: هذه إحدى نُظم المكتبات القديمة جدًا والتي كتبت تنفيذها بدايةً المكتبة <a href="http://requirejs.org/" rel="external nofollow">require.js</a>.
	</li>
	<li>
		<a href="http://wiki.commonjs.org/wiki/Modules/1.1" rel="external nofollow">CommonJS</a>: نظام الوِحدات الذي صُنِع لخوادم Node.js.
	</li>
	<li>
		<a href="https://github.com/umdjs/umd" rel="external nofollow">UMD</a>: نظام وِحدات آخر (اقتُرح ليكون للعموم أجمعين) وهو متوافق مع AMD وCommonJS.
	</li>
</ul>
<p>
	أمّا الآن فهذه المكتبات صارت (أو تصير، يومًا بعد آخر) جزءًا من التاريخ، ولكن مع ذلك سنراها في السكربتات القديمة.
</p>

<p>
	ظهر نظام الوِحدات (على مستوى اللغة) في المعيار عام 2015، وتطوّر شيئًا فشيئًا منذئذ وصارت الآن أغلب المتصفّحات الرئيسة (كما و Node.js) تدعمه. لذا سيكون أفضل لو بدأنا دراسة عملها من الآن.
</p>

<h2>
	ما الوحدة؟
</h2>

<p>
	الوِحدة هي ملف، فقط. كلّ نص برمجي يساوي وحدة واحدة.
</p>

<p>
	يمكن أن تُحمّل الوِحدات بعضها البعض وتستعمل توجيهات خاصة مثل التصدير <code>export</code> والاستيراد <code>import</code> لتتبادل الميزات فيما بينها وتستدعي الدوالّ الموجودة في وحدة ص، من وحدة س:
</p>

<ul>
<li>
		تقول الكلمة المفتاحية <code>export</code> للمتغيرات والدوالّ بأنّ الوصول إليها من خارج الوِحدة الحالية هو أمر مُتاح.
	</li>
	<li>
		وتُتيح <code>import</code> استيراد تلك الوظائف من الوِحدات الأخرى.
	</li>
</ul>
<p>
	فمثلًا لو كان لدينا الملف <code>sayHi.js</code> وهو يُصدّر دالّةً من الدوالّ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_7" style="">
<span class="com">// ? sayHi.js</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> sayHi</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">user</span><span class="pun">}!`);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	فيمكن لملف آخر استيراده واستعمالها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_9" style="">
<span class="com">// ? main.js</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">sayHi</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./sayHi.js'</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">sayHi</span><span class="pun">);</span><span class="pln"> </span><span class="com">// function... نوعها دالة</span><span class="pln">
sayHi</span><span class="pun">(</span><span class="str">'John'</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Hello, John!</span></pre>

<p>
	تتوجه تعليمة <code>import</code> للوِحدة <code>‎./sayHi.js</code> عبر المسار النسبي المُمرر لها. ويسند التابع <code>sayHi</code> للمتغيّر الذي يحمل نفس اسم التابع.
</p>

<p>
	لنشغّل المثال في المتصفّح.
</p>

<p>
	تدعم الوِحدات كلمات مفتاحية ومزايا خاصة، لذلك علينا إخبار المتصفّح بأنّ هذا السكربت هو وِحدة ويجب أن يُعامل بهذا النحو، ذلك باستعمال الخاصية <code>‎&lt;script type="module"&gt;‎</code>.
</p>

<p>
	هكذا:
</p>

<ul>
<li>
		ملف index.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6384_80" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"module"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">sayHi</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./say.js'</span><span class="pun">;</span><span class="pln">

  document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> sayHi</span><span class="pun">(</span><span class="str">'John'</span><span class="pun">);</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<ul>
<li>
		ملف say.js:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_13" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> sayHi</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">user</span><span class="pun">}!`;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجلب المتصفّح الوِحدة تلقائيًا ويقيم الشيفرة البرمجية بداخلها (ويستورد جميع الوحدات المتعلقة بها إن لزم الأمر)، وثمّ يشغلها وتكون نتيجة ما سبق:
</p>

<pre class="ipsCode">
Hello, John!
</pre>

<h2>
	ميزات الوحدات الأساسية
</h2>

<p>
	ولكن ما الفرق بين الوِحدات والسكربتات (الشيفرات) "العادية“ تلك؟
</p>

<p>
	للوِحدات ميزات أساسية تعمل على محرّكات جافاسكربت للمتصفّحات وللخوادم على حدّ سواء.
</p>

<h3>
	الوضع الصارم الإفتراضي
</h3>

<p>
	تستخدم الوِحدات الوضع الصارم تلقائيًا فمثلًا إسناد قيمة لمتحول غير معرّف سينتج خطأ.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6384_78" style="">
<span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"module"</span><span class="tag">&gt;</span><span class="pln">
  a </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<h3>
	النطاق على مستوى الوحدات
</h3>

<p>
	كلّ وِحدة لها نطاق عالي المستوى خاص بها. بتعبيرٍ آخر، لن يُنظر للمتغيّرات والدوالّ من الوحدات الأخرى، وإنما يكون نطاق المتغيرات محلي.
</p>

<p>
	نرى في المثال أدناه أنّا حمّلنا نصّين برمجيين، ويحاول الملف <code>hello.js</code> استعمال المتغير <code>user</code> المصرّح عنه في الملف <code>user.js</code> ولا يقدر:
</p>

<ul>
<li>
		ملف index.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6384_76" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"module"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"user.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"module"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"hello.js"</span><span class="tag">&gt;&lt;/script&gt;</span></pre>

<ul>
<li>
		ملف user.js:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_20" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">;</span></pre>

<ul>
<li>
		الملف hello.js:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_22" style="">
<span class="pln">alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">);</span></pre>

<p>
	لاحظ أن نتيجة ما سبق هي لا شيء لأن الدالة <code>alert</code> لم تتعرف على المتغير <code>user</code> الغير موجود، فكل وحدة لها متغيرات خاصة بها ويمكنها تصدير (عبر <code>export</code>) ما تريد للآخرين من خارجها رؤيته، واستيراد (عبر <code>import</code>) ما تحتاج استعماله. لذا علينا استيراد <code>user.js</code> و<code>hello.js</code> وأخذ المزايا المطلوبة منهما بدل الاعتماد على المتغيّرات العمومية.
</p>

<p>
	هذه النسخة الصحيحة من الشيفرة:
</p>

<ul>
<li>
		ملف index.html:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6384_74" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"module"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"hello.js"</span><span class="tag">&gt;&lt;/script&gt;</span></pre>

<ul>
<li>
		ملف user.js:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_26" style="">
<span class="kwd">export</span><span class="pln"> let user </span><span class="pun">=</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">;</span></pre>

<ul>
<li>
		الملف hello.js:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_28" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">user</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./user.js'</span><span class="pun">;</span><span class="pln">

document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> user</span><span class="pun">;</span><span class="pln"> </span><span class="com">// John</span></pre>

<p>
	يوجد في المتصفح نطاق مستقل عالي المستوى. وهو موجود أيضًا للوحدات <code>‎&lt;script type="module"&gt;‎</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6384_72" style="">
<span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"module"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="com">// سيكون المتغير مرئي في مجال هذه الوِحدة فقط</span><span class="pln">
  let user </span><span class="pun">=</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">;</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span><span class="pln">

</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"module"</span><span class="tag">&gt;</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫خطأ: المتغير user غير معرّف</span><span class="pln">

</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	ولو أردنا أن ننشئ متغيرًا عامًا على مستوى النافذة يمكننا تعيينه صراحة للمتغيّر <code>window</code> ويمكننا الوصول إليه هكذا <code>window.user</code>. ولكن لابد من وجود سبب وجيهٍ لذلك.
</p>

<h3>
	تقييم شيفرة الوِحدة لمرة واحدة فقط
</h3>

<p>
	لو استوردتَ نفس الوِحدة في أكثر من مكان، فلا تُنفّذ شيفرتها إلّا مرة واحدة، وبعدها تُصدّر إلى من استوردها.
</p>

<p>
	ولهذا توابع مهمّ معرفتها. لنرى بعض الأمثلة.
</p>

<p>
	أولًا، لو كان لشيفرة الوِحدة التي ستُنفّذ أيّ تأثيرات (مثل عرض رسالة أو ما شابه)، فاستيرادها أكثر من مرّة سيشغّل ذلك التأثير مرة واحدة، وهي أول مرة فقط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_32" style="">
<span class="com">// ? alert.js</span><span class="pln">
alert</span><span class="pun">(</span><span class="str">"Module is evaluated!"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫نُفّذت شيفرة الوِحدة!</span></pre>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_34" style="">
<span class="com">// نستورد نفس الوِحدة من أكثر من ملف</span><span class="pln">

</span><span class="com">// ? 1.js</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">`./</span><span class="pln">alert</span><span class="pun">.</span><span class="pln">js</span><span class="pun">`;</span><span class="pln"> </span><span class="com">// ‫نُفّذت شيفرة الوِحدة!</span><span class="pln">

</span><span class="com">// ? 2.js</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">`./</span><span class="pln">alert</span><span class="pun">.</span><span class="pln">js</span><span class="pun">`;</span><span class="pln"> </span><span class="com">// (لا نرى شيئًا هنا)</span></pre>

<p>
	في الواقع، فشيفرات الوِحدات عالية المستوى في بنية البرمجية لا تُستعمل إلّا لتمهيد بنى البيانات الداخلية وإنشائها. ولو أردنا شيئًا نُعيد استعماله، نُصدّر الوِحدة.
</p>

<p>
	الآن حان وقت مثال مستواه متقدّم أكثر.
</p>

<p>
	لنقل بأنّ هناك وحدة تُصدّر كائنًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_36" style="">
<span class="com">// ? admin.js</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> let admin </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	لو استوردنا هذه الوِحدة من أكثر من ملف، فلا تُنفّذ شيفرة الوِحدة إلّا أول مرة، حينها يُصنع كائن المدير <code>admin</code> ويُمرّر إلى كلّ من استورد الوِحدة.
</p>

<p>
	وهكذا تستلم كلّ الشيفرات كائن مدير <code>admin</code> واحد فقط لا أكثر ولا أقل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_38" style="">
<span class="com">// ? 1.js</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">admin</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./admin.js'</span><span class="pun">;</span><span class="pln">
admin</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Pete"</span><span class="pun">;</span><span class="pln">

</span><span class="com">// ? 2.js</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">admin</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./admin.js'</span><span class="pun">;</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">admin</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Pete</span></pre>

<p>
	كِلا الملفين ‎1.js و ‎2.js سيستوردان نفس الكائن ‫والتغييرات الّتي ستحدثُ في الملف ‎1.js ستكون مرئية في الملف ‎2.js.
</p>

<p>
	ولنؤكد مجددًا، تُنفذّ الوِحدة لمرة واحدة فقط. وتُنشئ الوِحدات المراد تصديرها وتُشارك بين المستوردين لذا فإن تغير شيء ما في كائن <code style="font-size: 16px;">admin</code> فسترى الوِحدات الأخرى ذلك.
</p>

<p>
	يتيح لنا هذا السلوك ”ضبط“ الوِحدة عند أوّل استيراد لها، فنضبط خاصياتها المرة الأولى، ومتى ما استوُردت مرة أخرى تكون جاهزة.
</p>

<p>
	فمثلًا قد تقدّم لنا وحدة <code>admin.js</code> بعض المزايا ولكن تطلب أن تأتي امتيازات الإدارة من خارج كائن <code>admin</code> إلى داخله:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_41" style="">
<span class="com">// ? admin.js</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> let admin </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> sayHi</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Ready</span><span class="pln"> to serve</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">admin</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}!`);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نضبط في <code>init.js</code> (أوّل نص برمجي لتطبيقنا) المتغير <code>admin.name</code>. بعدها سيراه كلّ من أراد بما في ذلك الاستدعاءات من داخل وحدة <code>admin.js</code> نفسها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_43" style="">
<span class="com">// ? init.js</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">admin</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./admin.js'</span><span class="pun">;</span><span class="pln">
admin</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Pete"</span><span class="pun">;</span></pre>

<p>
	ويمكن لوحدة أخرى استعمال <code>admin.name</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_45" style="">
<span class="com">// ? other.js</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">admin</span><span class="pun">,</span><span class="pln"> sayHi</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./admin.js'</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">admin</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Pete</span><span class="pln">

sayHi</span><span class="pun">();</span><span class="pln"> </span><span class="com">// Ready to serve, Pete!</span></pre>

<h3>
	import.meta
</h3>

<p>
	يحتوي الكائن <code>import.meta</code> على معلومات الوِحدة الحالية.
</p>

<p>
	ويعتمد محتواها على البيئة الحالية، ففي المتصفّحات يحتوي على عنوان النص البرمجي أو عنوان صفحة الوِب الحالية لو كان داخل HTML:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_47" style="">
<span class="pun">&lt;</span><span class="pln">script type</span><span class="pun">=</span><span class="str">"module"</span><span class="pun">&gt;</span><span class="pln">
  alert</span><span class="pun">(</span><span class="kwd">import</span><span class="pun">.</span><span class="pln">meta</span><span class="pun">.</span><span class="pln">url</span><span class="pun">);</span><span class="pln"> 
  </span><span class="com">// ‫عنوان URL للسكربت (عنوان URL لصفحة HTML للسكربت الضمني)</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<h3>
	<code>this</code> في الوِحدات ليست معرّفة
</h3>

<p>
	قد تكون هذه الميزة صغيرة، ولكنّا سنذكرها ليكتمل هذا الفصل.
</p>

<p>
	في الوحدات، قيمة <code>this</code> عالية المستوى غير معرّفة.
</p>

<p>
	وازن بينها وبين السكربتات غير المعتمدة على الوحدات، إذ ستكون <code>this</code> كائنًا عامًا:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6384_69" style="">
<span class="tag">&lt;script&gt;</span><span class="pln">
  alert</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">);</span><span class="pln"> </span><span class="com">// window</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span><span class="pln">

</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"module"</span><span class="tag">&gt;</span><span class="pln">
  alert</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">);</span><span class="pln"> </span><span class="com">// غير معرّف</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<h2>
	الميزات الخاصة بالمتصفحات
</h2>

<p>
	كما أن هناك عدّة فروق تخصّ المتصفحات السكربتات (المعتمدة على الوحدات) بالنوع <code>type="module"‎</code> موازنةً بتلك العادية.
</p>

<p>
	لو كنت تقرأ هذا الفصل لأول مرة، أو لم تكن تستعمل المحرّك في المتصفّح فيمكنك تخطّي هذا القسم.
</p>

<h3>
	سكربتات الوِحدات مؤجلة
</h3>

<p>
	دائمًا ما تكون سكربتات الوِحدات مؤجلة، ومشابهة لتأثير السِمة <code>defer</code> (الموضحة في هذا <a href="https://javascript.info/script-async-defer" rel="external nofollow">المقال</a>)، لكل من السكربتات المضمّنة والخارجية.
</p>

<p>
	أي وبعبارة أخرى:
</p>

<ul>
<li>
		تنزيل السكربتات المعتمدة على الوِحدات الخارجية <code>‎&lt;script type="module" src=‎"..."&gt;‎</code> لا تُوقف معالجة HTML فتُحمّل بالتوازي مع الموارد الأخرى.
	</li>
	<li>
		تنتظر السكربتات المعتمدة على الوِحدات حتّى يجهز مستند HTML تمامًا (حتّى لو كانت صغيرة وحُمّلت بنحوٍ أسرع من HTML) وتُشغّل عندها.
	</li>
	<li>
		تحافظ على الترتيب النسبي للسكربتات: فالسكربت ذو الترتيب الأول ينفذّ أولًا.
	</li>
</ul>
<p>
	ويسبّب هذا بأن ”ترى“ السكربتات المعتمدة على الوِحدات صفحة HTML المحمّلة كاملة بما فيه عناصر الشجرة أسفلها.
</p>

<p>
	مثال:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6384_67" style="">
<span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"module"</span><span class="tag">&gt;</span><span class="pln">


  alert</span><span class="pun">(</span><span class="kwd">typeof</span><span class="pln"> button</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫كائن (object): يستطيع السكربت رؤية العناصر أدناه</span><span class="pln">

  </span><span class="com">// بما أن الوِحدات مؤجلة. سيُشغل السكربت بعد تحميل كامل الصفحة</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span><span class="pln">

Compare to regular script below:

</span><span class="tag">&lt;script&gt;</span><span class="pln">


  alert</span><span class="pun">(</span><span class="kwd">typeof</span><span class="pln"> button</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫خطأ: الزر (button) غير معرّف. لن يستطيع السكربت رؤية العناصر أدناه</span><span class="pln">

  </span><span class="com">// السكربت العادي سيُشغل مباشرة قبل أن يُستكمل تحميل الصفحة </span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span><span class="pln">

</span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"button"</span><span class="tag">&gt;</span><span class="pln">Button</span><span class="tag">&lt;/button&gt;</span></pre>

<p>
	لاحِظ كيف أنّ النص البرمجي الثاني يُشغّل فعليًا قبل الأول! لذا سنرى أولًا <code>undefined</code> وبعدها <code>object</code>. وذلك بسبب كون عملية تشغيل الوِحدات مُؤجلة لذلك سننتظر لاكتمال معالجة المستند. نلاحظ أن السكربت العادي سيُشغلّ مباشرة بدون تأجيل ولذا سنرى نتائجه أولًا.
</p>

<p>
	علينا أن نحذر حين نستعمل الوِحدات إذ أنّ صفحة HTML تظهر بينما الوِحدات تُحمّل، وبعدها تعمل الوحدات. بهذا يمكن أن يرى المستخدم أجزاءً من الصفحة قبل أن يجهز تطبيق جافاسكربت، ويرى بأنّ بعض الوظائف في الموقع لا تعمل بعد. علينا هنا وضع ”مؤشّرات تحميل“ أو التثبّت من أنّ الزائر لن يتشتّت بهذا الأمر.
</p>

<h3>
	خاصية Async على السكربتات المضمّنة
</h3>

<p>
	بالنسبة للسكربتات غير المعتمدة على الوِحدات فإن خاصية <code>async</code> (اختصارًا لكلمة Asynchronous أي غير المتزامن) تعمل على السكربتات الخارجية فقط. وتُشغل السكربتات غير المتزامنة مباشرة عندما تكون جاهزة، بشكل مستقل عن السكربتات الأخرى أو عن مستند HTML.
</p>

<p>
	تعمل السكربتات المعتمدة على الوِحدات طبيعيًا في السكربتات المضمّنة.
</p>

<p>
	فمثلًا يحتوي السكربت المُضمن أدناه على الخاصية <code>async</code>، لذلك سيُشغّل مباشرة ولن ينتظر أي شيء.
</p>

<p>
	وهو ينفذ عملية الاستيراد (اجلب الملف <code>‎./analytics.js</code>) وشغله عندما يصبح جاهزًا، حتى وإن لم ينتهِ مستند HTML بعد. أو السكربتات الأُخرى لا تزال معلّقة.
</p>

<p>
	وهذا جيد للتوابع المستقلة مثل العدادات والإعلانات ومستمع الأحداث على مستوى المستند.
</p>

<p>
	في المثال أدناه، جُلبت جميع التبعيات (من ضمنها analytics.js).‫ ومن ثمّ شُغّل السكربت ولم ينتظر حتى اكتمال تحميل المستند أو السكربتات الأخرى.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6384_59" style="">
<span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">async</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"module"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">counter</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./analytics.js'</span><span class="pun">;</span><span class="pln">

  counter</span><span class="pun">.</span><span class="pln">count</span><span class="pun">();</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<h3>
	السكربتات الخارجية
</h3>

<p>
	تختلف السكربتات الخارجية التي تحتوي على السمة <code>type="module"‎</code> في جانبين:
</p>

<ol>
<li>
		<p>
			تنفذ السكربتات الخارجية التي لها نفس القيمة للخاصية <code>src</code> مرة واحدة فقط. فهنا مثلًا سيُجلب السكربت <code>my.js</code> وينفذ مرة واحدة فقط.
		</p>

		<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_55" style="">
<span class="pun">&lt;</span><span class="pln">script type</span><span class="pun">=</span><span class="str">"module"</span><span class="pln"> src</span><span class="pun">=</span><span class="str">"my.js"</span><span class="pun">&gt;&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">script type</span><span class="pun">=</span><span class="str">"module"</span><span class="pln"> src</span><span class="pun">=</span><span class="str">"my.js"</span><span class="pun">&gt;&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

		<p>
			 
		</p>
	</li>
	<li>
		<p>
			تتطلب السكربتات الخارجية التي تجلب من مصدر مستقل (موقع مختلف عن الأساسي) ترويسات <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" rel="external nofollow">CORS</a> والموضحة في هذا <a href="https://javascript.info/fetch-crossorigin" rel="external nofollow">المقال</a>. بتعبير آخر إن جُلِبَ سكربت يعتمد على الوِحدات من مصدر معين فيجب على الخادم البعيد أن يدعم ترويسات السماح بالجلب <code>Access-Control-Allow-Origin</code>. يجب أن يدعم المصدر المستقل <code>Access-Control-Allow-Origin</code> (في المثال أدناه المصدر المستقل هو another-site.com) وإلا فلن يعمل السكربت. 
		</p>

		<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6384_57" style="">
<span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"module"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"http://another-site.com/their.js"</span><span class="tag">&gt;&lt;/script&gt;</span></pre>
		<script type="module" src="http://another-site.com/their.js"></script>
</li>
</ol>
<p>
	وذلك سيضمن لنا مستوى أمان أفضل إفتراضيًا.
</p>

<h3>
	لا يُسمح بالوحدات المجردة
</h3>

<p>
	في المتصفح، يجب أن تحصل تعليمة <code>import</code> على عنوان URL نسبي أو مطلق. وتسمى الوِحدات التي بدون أي مسار بالوحدات المجردة. وهي ممنوع في تعليمة <code>import</code>.
</p>

<p>
	لنأخذ مثالًا يوضح الأمر، هذا <code>import</code> غير صالح:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6384_61" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">sayHi</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'sayHi'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ وِحدة مجردة</span><span class="pln">
</span><span class="com">// ‫يجب أن تمتلك الوِحدة مسارًا مثل: '‎./sayHi.js' أو مهما يكُ موقع هذه الوِحدة</span></pre>

<p>
	تسمح بعض البيئات، مثل Node.js أو أدوات تجميع الوِحدات باستخدام الوِحدات المجردة، دون أي مسار، حيث أن لديها طرقها الخاصة للعثور على الوِحدات والخطافات لضبطها. ولكن حتى الآن لا تدعم المتصفحات الوِحدات المجردة.
</p>

<h3>
	التوافقية باستخدام "nomodule"
</h3>

<p>
	لا تفهم المتصفحات القديمة طريقة استخدام الوِحدات في الصفحات <code>type ="module"‎</code>.بل وإنها تتجاهل السكربت ذو النوعٍ غير المعروف. بالنسبة لهم، من الممكن تقديم نسخة مخصصة لهم باستخدام السمة <code>nomodule</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6384_63" style="">
<span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"module"</span><span class="tag">&gt;</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">"Runs in modern browsers"</span><span class="pun">);</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span><span class="pln">

</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">nomodule</span><span class="tag">&gt;</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">"Modern browsers know both type=module and nomodule, so skip this"</span><span class="pun">):</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">"Old browsers ignore script with unknown type=module, but execute this."</span><span class="pun">);</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	‫المتصفحات الحديثة تعرف <code>type=module</code> و <code>nomodule</code> لذا لن تنفذ الأخير بينما ستتجاهل ‫المتصفحات القديمة الوسم ذو السِمة <code>type=module</code> ولكن ستنفذ وسم <code>nomodule</code>.
</p>

<h2>
	أدوات البناء
</h2>

<p>
	في الحياة الواقعية، نادرًا ما تستخدم وحدات المتصفح في شكلها "الخام". بل عادةّ نجمعها مع أداة خاصة مثل <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-webpack-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r866//" rel="">Webpack</a> وننشرها على خادم النشر.
</p>

<p>
	إحدى مزايا استخدام المجمعات - فهي تمنح المزيد من التحكم في كيفية التعامل مع الوحدات، مما يسمح بالوحدات المجردة بل وأكثر من ذلك بكثير، مثل وحدات HTML/CSS.
</p>

<p>
	تؤدي أدوات البناء بعض الوظائف منها:
</p>

<ol>
<li>
		جلب الوِحدة الرئيسية <code>main</code>، وهي الوِحدة المراد وضعها في وسم <code>‎&lt;script type ="module"&gt;‎</code> في ملف HTML.
	</li>
	<li>
		تحليل التبعيات: تحليل تعليمات الاستيراد الخاصة بالملف الرئيسي وثم للملفات المستوردة أيضًا وما إلى ذلك.
	</li>
	<li>
		إنشاء ملفًا واحدًا يحتوي على جميع الوِحدات (مع إمكانية تقسيمهُ لملفات متعددة)، مع استبدال تعليمة <code>import</code> الأصلية بتوابع الحزم لكي يعمل السكربت. كما تدعم أنواع وحدات "خاصة" مثل وحدات HTML/CSS.
	</li>
	<li>
		يمكننا تطبيق عمليات تحويل وتحسينات أخرى في هذه العملية مثل:
		<ul>
<li>
				إزالة الشيفرات الّتي يتعذر الوصول إليها.
			</li>
			<li>
				إزالة تعليمات التصدير غير المستخدمة (مشابهة لعملية هز الأشجار وسقوط الأوراق اليابسة).
			</li>
			<li>
				إزالة العبارات الخاصة بمرحلة التطوير مثل <code>console</code> و<code>debugger</code>.
			</li>
			<li>
				تحويل شيفرة جافاسكربت الحديثة إلى شيفرة أقدم باستخدام وظائف مماثلة للحزمة <a href="https://babeljs.io/" rel="external nofollow">Babel</a>.
			</li>
			<li>
				تصغير الملف الناتج (إزالة المسافات، واستبدال المتغيرات بأسماء أقصر، وما إلى ذلك).
			</li>
		</ul>
</li>
</ol>
<p>
	عند استخدامنا لأدوات التجميع سيُجمع السكربت ليصبح في ملف واحد (أو ملفات قليلة) ، تُستبدل تعليمات <code>import/export</code> بداخل السكربتات بتوابع المُجمّع الخاصة. لذلك لا يحتوي السكربت "المُجَمّع" الناتج على أي تعليمات <code>import/export</code>، ولا يتطلب السِمة <code>type="module"‎</code>، ويمكننا وضعه في سكربت عادي: في المثال أدناه لنفترض أننا جمعّنا الشيفرات في ملف bundle.js باستخدام مجمع حزم مثل: Webpack.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6384_65" style="">
<span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"bundle.js"</span><span class="tag">&gt;&lt;/script&gt;</span></pre>

<p>
	ومع ذلك يمكننا استخدام الوِحدات الأصلية (في شكلها الخام). لذلك لن نستخدم هنا أداة Webpack: يمكنك التعرف عليها وضبطها لاحقًا.
</p>

<h2>
	خلاصة
</h2>

<p>
	لنلخص المفاهيم الأساسية:
</p>

<ol>
<li>
		الوِحدة هي مجرد ملف. لجعل تعليمتي <code>import/export</code> تعملان، ستحتاج المتصفحات إلى وضع السِمة التالية <code>‎&lt;script type ="module"&gt;‎</code>. تحتوي الوِحدات على عدة مُميزات:

		<ul>
<li>
				مؤجلة إفتراضيًا.
			</li>
			<li>
				تعمل الخاصية Async على السكربتات المضمّنة.
			</li>
			<li>
				لتحميل السكربتات الخارجية من مصدر مستقل، يجب استخدام طريقة (المَنفذ / البروتوكول / المجال)، وسنحتاج لترويسات CORS أيضًا.
			</li>
			<li>
				ستُتجاهل السكربتات الخارجية المكررة.
			</li>
		</ul>
</li>
	<li>
		لكل وِحدة من الوِحدات نطاق خاص بها، وتتبادلُ الوظائف فيما بينها من خلال استيراد وتصدير الوِحدات <code>import/export</code>.
	</li>
	<li>
		تستخدم الوِحدات الوضع الصارم دومًا <code>use strict</code>.
	</li>
	<li>
		تُنفذ شيفرة الوِحدة لمرة واحدة فقط. وتُصدر إلى من استوردها لمرة واحدة أيضًا، ومن ثمّ تُشارك بين المستوردين.
	</li>
</ol>
<p>
	عندما نستخدم الوحدات، تنفذ كل وِحدة وظيفة معينة وتُصدرها. ونستخدم تعليمة <code>import</code> لاستيرادها مباشرة عند الحاجة. إذ يُحمل المتصفح السكربت ويقيّمه تلقائيًا.
</p>

<p>
	وبالنسبة لوضع النشر، غالبًا ما يستخدم الناس مُحزّم الوِحدات مثل <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-webpack-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r866/" rel="">Webpack</a> لتجميع الوِحدات معًا لرفع الأداء ولأسباب أخرى.
</p>

<p>
	سنرى في الفصل التالي مزيدًا من الأمثلة عن الوِحدات، وكيفية تصديرها واستيرادها.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/modules-intro" rel="external nofollow">Modules, introduction</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">926</guid><pubDate>Thu, 13 Aug 2020 18:09:01 +0000</pubDate></item><item><title>&#x627;&#x644;&#x645;&#x643;&#x631;&#x631;&#x627;&#x62A; iterators &#x648;&#x627;&#x644;&#x645;&#x648;&#x644;&#x62F;&#x627;&#x62A; generators &#x63A;&#x64A;&#x631; &#x627;&#x644;&#x645;&#x62A;&#x632;&#x627;&#x645;&#x646;&#x629; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%88%D8%A7%D9%84%D9%85%D9%88%D9%84%D8%AF%D8%A7%D8%AA-generators-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r923/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/79.jpg.0e75b964c1f8bfc18324f5a357f8bef4.jpg" /></p>

<p>
	تُتيح لنا المُكرّرات غير المتزامنة المرور على البيانات التي تأتينا على نحوٍ غير متزامن متى لزم، مثل حين نُنزّل شيئًا كتلةً كتلةً عبر الشبكة. المولّدات غير المتزامنة تجعل من ذلك أسهل فأسهل.
</p>

<p>
	لنرى مثالًا أولًا لنفهم الصياغة، بعدها نرى مثالًا من الحياة العملية.
</p>

<h2>
	المكررات غير المتزامنة
</h2>

<p>
	تتشابه المُكرّرات غير المتزامنة مع تلك العادية، بفروق بسيطة في الصياغة.
</p>

<p>
	فكائن المُكرّر العادي (كما رأينا في الفصل <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%83%D8%B1%D9%91%D9%8E%D8%B1%D8%A9-iterables-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r820/" rel="">الكائنات المكرَّرة (Iterables) في جافاسكربت</a>) يكون هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5675_7" style="">
<span class="pln">let range </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  from</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  to</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
  </span><span class="com">// ‫تستدعي حلقة for..of هذه الدالّة مرة واحدة في البداية</span><span class="pln">

  </span><span class="pun">[</span><span class="typ">Symbol</span><span class="pun">.</span><span class="pln">iterator</span><span class="pun">]()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="com">// ‫...ستعيد كائن مُكرُر:</span><span class="pln">
    </span><span class="com">// ‫لاحقًا ستعمل حلقة for..of فقط مع ذلك الكائن.</span><span class="pln">
    </span><span class="com">//  ‫وتطلب منه القيم التالية باستخدام دالة next()‎</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      current</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">from</span><span class="pun">,</span><span class="pln">
      last</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">to</span><span class="pun">,</span><span class="pln">

      </span><span class="com">// ‫تُستدعى next()‎ في كلّ تكرار من خلال الحلقة for..of</span><span class="pln">

      next</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (2)</span><span class="pln">
        </span><span class="com">// ‫يجب أن تعيد القيم ككائن {done:.., value :...}</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">current </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">last</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> done</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">current</span><span class="pun">++</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> done</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let value of range</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1 then 2, then 3, then 4, then 5</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	راجِع … لتعرف تفاصيل المُكرّرات العادية، لو لزم.
</p>

<p>
	ولنجعل الكائن مُتكرّرًا غير متزامنًا:
</p>

<ol>
<li>
		علينا استعمال <code>Symbol.asyncIterator</code> بدل <code>Symbol.iterator</code>.
	</li>
	<li>
		على <code>next()‎</code> إعادة وعد.
	</li>
	<li>
		علينا استعمال حلقة <code>for await (let item of iterable)‎</code> لتكرار كائن معين.
	</li>
</ol>
<p>
	فلنصنع كائن <code>range</code> مُتكرّرًا (كما أعلاه) ولكن يُعيد القيم بنحوٍ غير متزامن، قيمةً واحدةً كلّ ثانية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5675_9" style="">
<span class="pln">let range </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  from</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  to</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
  </span><span class="com">// ‫تستدعي حلقة for..of هذه الدالّة مرة واحدة في البداية</span><span class="pln">

  </span><span class="pun">[</span><span class="typ">Symbol</span><span class="pun">.</span><span class="pln">asyncIterator</span><span class="pun">]()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (1)</span><span class="pln">

    </span><span class="com">// ‫...ستعيد كائن مُكرُر:</span><span class="pln">
    </span><span class="com">// ‫لاحقًا ستعمل حلقة for..of فقط مع ذلك الكائن.</span><span class="pln">
    </span><span class="com">//  ‫وتطلب منه القيم التالية باستخدام دالة next()‎</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      current</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">from</span><span class="pun">,</span><span class="pln">
      last</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">to</span><span class="pun">,</span><span class="pln">
      </span><span class="com">// ‫تُستدعى next()‎ في كلّ تكرار من خلال الحلقة for..of</span><span class="pln">

      async next</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (2)</span><span class="pln">
        </span><span class="com">// ‫يجب أن تعيد القيم ككائن {done:.., value :...}</span><span class="pln">
        </span><span class="com">// وستُغلّف تلقائيًا في وعد غير متزامن</span><span class="pln">



        </span><span class="com">// ‫يمكننا استخدام await لتنفيذ أشياء غير متزامنة:</span><span class="pln">
        await </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">));</span><span class="pln"> </span><span class="com">// (3)</span><span class="pln">


        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">current </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">last</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> done</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">current</span><span class="pun">++</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> done</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="pun">(</span><span class="pln">async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">


  </span><span class="kwd">for</span><span class="pln"> await </span><span class="pun">(</span><span class="pln">let value of range</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (4)</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1,2,3,4,5</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">


</span><span class="pun">})()</span></pre>

<p>
	كما ترى فالبنية تشبه المُكرّرات العادية:
</p>

<ol>
<li>
		ليكون الكائن مُتكرّرًا على نحوٍ غير متزامن، يجب أن يحتوي التابِع <code>Symbol.asyncIterator</code> (لاحِظ <code>(1)</code>).
	</li>
	<li>
		على التابِع إعادة الكائن وهو يحتوي التابِع <code>next()‎</code> الذي يُعيد وعدًا (لاحِظ <code>(2)</code>).
	</li>
	<li>
		ليس على التابِع <code>next()‎</code> أن يكون غير متزامن <code>async</code>. فيمكن أن يكون تابِعًا عاديًا يُعيد وعدًا، إلّا أنّ <code>async</code> تُتيح لنا استعمال <code>await</code> وهذا أفضل لنا. هنا نؤخّر التنفيذ ثانيةً واحدةً فقط (لاحِظ <code>(3)</code>).
	</li>
	<li>
		ليحدث التكرار نستعمل <code>for await(let value of range)‎</code> (لاحِظ <code>(4)</code>)، أي نُضيف <code>await</code> قبل الحلقة <code>for</code>. هكذا تستدعي الحلقة <code>range[Symbol.asyncIterator]()‎</code> مرةً واحدة، وبعدها تابِع <code>next()‎</code> للقيم التالية.
	</li>
</ol>
<p>
	إليك ورقة صغيرة تغشّ منها:
</p>

<table>
<thead><tr>
<th>
				 
			</th>
			<th>
				المُكرّرات
			</th>
			<th>
				المُكرّرات غير المتزامنة
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				تابِع الكائن الذي يُقدّم المُكرّر
			</td>
			<td>
				<code>Symbol.iterator</code>
			</td>
			<td>
				<code>Symbol.asyncIterator</code>
			</td>
		</tr>
<tr>
<td>
				قيمة <code>next()</code> المُعادة هي
			</td>
			<td>
				أيّ قيمة
			</td>
			<td>
				وعد <code>Promise</code>
			</td>
		</tr>
<tr>
<td>
				to loop, use
			</td>
			<td>
				<code>for..of</code>
			</td>
			<td>
				<code>for await..of</code>
			</td>
		</tr>
</tbody>
</table>
<p>
	<strong>تحذير</strong>: "لا يعمل معامل البقية <code>...</code> بطريقة غير متزامنة" الميزات التي تتطلب تكرارات منتظمة ومتزامنة، لا تعمل مع تلك المتزامنة.
</p>

<p>
	على سبيل المثال، لن يعمل معامل البقية هنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5675_11" style="">
<span class="pln">alert</span><span class="pun">(</span><span class="pln"> </span><span class="pun">[...</span><span class="pln">range</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// Error, no Symbol.iterator</span></pre>

<p>
	هذا أمر طبيعي، لأنه يتوقع العثور على <code>Symbol.iterator</code>، مثل <code>for..of</code> بدون <code>await</code>. ولكن ليس <code>Symbol.asyncIterator</code>.
</p>

<h2>
	المولدات غير المتزامنة
</h2>

<p>
	كما نعلم فلغة جافاسكربت تدعم المولّدات أيضًا، وهذه المولّدات مُتكرّرة.
</p>

<p>
	فلنتذكّر مولّد السلاسل من الفصل <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D9%88%D9%84%D8%AF%D8%A7%D8%AA-generators-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r922/" rel="">المولِّدات</a>كان يولّد سلسلة من القيم من متغير البداية <code>start</code> إلى متغير النهاية <code>end</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5675_13" style="">
<span class="kwd">function</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">(</span><span class="pln">start</span><span class="pun">,</span><span class="pln"> end</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> end</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    yield i</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let value of generateSequence</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1, then 2, then 3, then 4, then 5</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لا يمكننا في الدوال العادية استعمال <code>await</code>، فعلى كلّ القيم أن تأتي بنحوٍ متزامن وليس ممكنًا وضع أيّ تأخير في حلقة <code>for..of</code>،
</p>

<p>
	ولكن، ماذا لو أردنا استعمال <code>await</code> في المولّد؟ مثلًا لنُرسل الطلبات عبر الشبكة؟
</p>

<p>
	لا مشكلة، نضع قبله <code>async</code> هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5675_15" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">(</span><span class="pln">start</span><span class="pun">,</span><span class="pln"> end</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> end</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">


    </span><span class="com">// yay, can use await!</span><span class="pln">
    await </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">));</span><span class="pln">


    yield i</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln">

</span><span class="pun">(</span><span class="pln">async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  let generator </span><span class="pun">=</span><span class="pln"> generateSequence</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> await </span><span class="pun">(</span><span class="pln">let value of generator</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1, then 2, then 3, then 4, then 5</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">})();</span></pre>

<p>
	الآن لدينا مولّد غير متزامن، وقابل للتكرار مع <code>for await...of</code>.
</p>

<p>
	إنها حقًا بسيطة للغاية. نضيف الكلمة المفتاحية <code>async</code>، ويستطيع المولّد الآن استخدام <code>await</code> بداخله، ويعتمد على الوعود والدوالّ غير متزامنة الأخرى.
</p>

<p>
	وتقنيًا، الفرق الآخر للمولّد غير المتزامن هو أنّ تابِع <code>generator.next()‎</code> صار غير متزامنًا أيضًا ويُعيد الوعود.
</p>

<p>
	في المولّدات العادية نستعمل <code>result = generator.next()‎</code> لنأخذ القيم، بينما في المولّدات غير المتزامنة نُضيف <code>await</code> هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5675_17" style="">
<span class="pln">result </span><span class="pun">=</span><span class="pln"> await generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">();</span><span class="pln"> </span><span class="com">// result = {value: ..., done: true/false}</span></pre>

<h2>
	المكررات غير المتزامنة
</h2>

<p>
	كما نعلم ليكون الكائن مُتكرّرًا علينا إضافة رمز <code>Symbol.iterator</code> إليه.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5675_19" style="">
<span class="pln">let range </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  from</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  to</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">

  </span><span class="pun">[</span><span class="typ">Symbol</span><span class="pun">.</span><span class="pln">iterator</span><span class="pun">]()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">object </span><span class="kwd">with</span><span class="pln"> next to make range iterable</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	هناك أسلوب شائع هو إعادة <code>Symbol.iterator</code> لمولّد بدل كائن صِرف يحمل التابِع <code>next</code> كما المثال أعلاه.
</p>

<p>
	لنستذكر معًا المثال من الفصل <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D9%88%D9%84%D8%AF%D8%A7%D8%AA-generators-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r922/" rel="">المولِّدات</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5675_21" style="">
<span class="pln">let range </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  from</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  to</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">

  </span><span class="pun">*[</span><span class="typ">Symbol</span><span class="pun">.</span><span class="pln">iterator</span><span class="pun">]()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// ‫اختصارًا لـِ ‎[Symbol.iterator]: function*()‎</span><span class="pln">
    </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let value </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">from</span><span class="pun">;</span><span class="pln"> value </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">to</span><span class="pun">;</span><span class="pln"> value</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      yield value</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let value of range</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1, then 2, then 3, then 4, then 5</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نرى الكائن المخصّص <code>range</code> هنا مُتكرّرًا، والمولّد <code>‎*[Symbol.iterator]‎</code> يؤدّي المنطق اللازم لسرد القيم.
</p>

<p>
	لو أردنا إضافة أيّ إجراءات غير متزامنة للمولّدة، فعلينا استبدال الرمز <code>Symbol.iterator</code> بالرمز <code>Symbol.asyncIterator</code> غير المتزامن.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5675_23" style="">
<span class="pln">let range </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  from</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  to</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">


  async </span><span class="pun">*[</span><span class="typ">Symbol</span><span class="pun">.</span><span class="pln">asyncIterator</span><span class="pun">]()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// ‫مشابه لـِ ‎[Symbol.asyncIterator]: async function*()‎</span><span class="pln">

    </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let value </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">from</span><span class="pun">;</span><span class="pln"> value </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">to</span><span class="pun">;</span><span class="pln"> value</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

      </span><span class="com">// توقف بين القيم لانتظار شيء ما </span><span class="pln">
      await </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">));</span><span class="pln">

      yield value</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="pun">(</span><span class="pln">async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">for</span><span class="pln"> await </span><span class="pun">(</span><span class="pln">let value of range</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1, then 2, then 3, then 4, then 5</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">})();</span></pre>

<p>
	الآن تأتينا القيم متأخرة عن بعضها البعض ثانيةً واحدة.
</p>

<h2>
	مثال من الحياة العملية
</h2>

<p>
	حتى اللحظة كانت الأمثلة كلّها بسيطة، لنفهم القصة فقط. الآن حان الوقت لنُطالع مثالًا وحالةً من الواقع.
</p>

<p>
	نرى على الإنترنت خدمات كثيرة تقدّم لنا البيانات على صفحات (paginated). فمثلًا حين نطلب قائمة من المستخدمين يُعيد الطلب عددًا تحدّد مسبقًا (مثلًا 100 مستخدم)، أي ”صفحة واحدة“، ويعطينا أيضًا عنوان الصفحة التالية.
</p>

<p>
	هذا النمط شائع جدًا بين المطوّرين، وليس مستخدمًا للمستخدمين فقط بل لكلّ شيء. فمثلًا غِت‌هَب تتيح لك جلب الإيداعات بنفس الطريقة باستعمال الصفحات:
</p>

<ul>
<li>
		عليك إرسال طلب إلى المسار <code><a href="https://api.github.com/repos/&lt;repo&gt;/commits" ipsnoembed="false" rel="external nofollow">https://api.github.com/repos/<repo>/commits</repo></a></code>.
	</li>
	<li>
		يستجيب الخادوم بكائن JSON فيه 30 إيداعًا، ويُعطيك رابطًا يوصلك إلى الصفحة التالية في ترويسة <code>Link</code>.
	</li>
	<li>
		بعدها نستعمل ذلك الرابط للطلبات التالية لنجلب إيداعات أكثر، وهكذا.
	</li>
</ul>
<p>
	ولكن ما نريد هو واجهة برمجية أبسط قليلًا: أي كائنًا مُتكرّرًا فيه الإيداعات كي نمرّ عليها بهذا النحو:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5675_25" style="">
<span class="pln">let repo </span><span class="pun">=</span><span class="pln"> </span><span class="str">'javascript-tutorial/en.javascript.info'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// المستودع الذي فيه الإيداعات التي نريد على غِت‌هَب</span><span class="pln">

</span><span class="kwd">for</span><span class="pln"> await </span><span class="pun">(</span><span class="pln">let commit of fetchCommits</span><span class="pun">(</span><span class="pln">repo</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// نُعالج كلّ إيداع commit</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ونريد كتابة الدالة <code>fetchCommits(repo)‎</code> لتجلب تلك الإيداعات لنا وتؤدّي الطلبات اللازمة متى… لزم. كما ونترك في عهدتها مهمة الصفحات كاملةً. ما سنفعله من جهتنا هو أمر بسيط، <code>for await..of</code> فقط.
</p>

<p>
	باستعمال المولّدات غير المتزامنة فهذه المهمّة تصير سهلة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5675_27" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pun">*</span><span class="pln"> fetchCommits</span><span class="pun">(</span><span class="pln">repo</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let url </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">https</span><span class="pun">:</span><span class="com">//api.github.com/repos/${repo}/commits`;</span><span class="pln">

  </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (1)</span><span class="pln">
      headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="str">'User-Agent'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Our script'</span><span class="pun">},</span><span class="pln"> 
    </span><span class="pun">});</span><span class="pln">

    </span><span class="kwd">const</span><span class="pln"> body </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln"> </span><span class="com">// (2) </span><span class="pln">

    </span><span class="com">// (3) </span><span class="pln">
    let nextPage </span><span class="pun">=</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">headers</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'Link'</span><span class="pun">).</span><span class="pln">match</span><span class="pun">(</span><span class="str">/&lt;(.*?)&gt;; rel="next"/</span><span class="pun">);</span><span class="pln">
    nextPage </span><span class="pun">=</span><span class="pln"> nextPage </span><span class="pun">&amp;&amp;</span><span class="pln"> nextPage</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span><span class="pln">

    url </span><span class="pun">=</span><span class="pln"> nextPage</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let commit of body</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (4) </span><span class="pln">
      yield commit</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<ol>
<li>
		نستعمل تابِع <a href="https://javascript.info/fetch" rel="external nofollow">fetch</a> من المتصفّح لتنزيل العنوان البعيد. يُتيح لنا التابِع تقديم تصاريح الاستيثاق وغيرها من ترويسات مطلوبة. غِت‌هَب يطلب <code>User-Agent</code>.
	</li>
	<li>
		نحلّل نتيجة الجلب على أنّها كائن JSON، وهذا تابِع آخر خاصّ بالتابِع <code>fetch</code>.
	</li>
	<li>
		نستلم هكذا عنوان الصفحة التالية من ترويسة <code>Link</code> داخل الردّ. لهذا العنوان تنسيق خاص فنستعمل تعبيرًا نمطيًا لتحليله. يظهر عنوان الصفحة التالية على هذا الشكل: <code><a href="https://api.github.com/repositories/93253246/commits?page=2" ipsnoembed="false" rel="external nofollow">https://api.github.com/repositories/93253246/commits?page=2</a></code>، وموقع غِت‌هَب هو من يولّده بنفسه.
	</li>
	<li>
		بعدها نُنتِج كلّ الإيداعات التي استلمناها، ومتى انتهت
	</li>
</ol>
<p>
	مثال على طريقة الاستعمال (تعرض من كتبَ الإيداعات في الطرفيّة):
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5675_29" style="">
<span class="pun">(</span><span class="pln">async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  let count </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">for</span><span class="pln"> await </span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> commit of fetchCommits</span><span class="pun">(</span><span class="str">'javascript-tutorial/en.javascript.info'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">commit</span><span class="pun">.</span><span class="pln">author</span><span class="pun">.</span><span class="pln">login</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(++</span><span class="pln">count </span><span class="pun">==</span><span class="pln"> </span><span class="lit">100</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// لنتوقف عند 100 إيداع</span><span class="pln">
      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">})();</span></pre>

<p>
	كما نريد تمامًا. الطريقة التي تعمل بها الطلبات بهذا النوع لا تظهر للشيفرات الخارجية وتبقى تفاصيل داخلية. المهم لنا هي استعمال مولّد غير متزامن يُعيد الإيداعات.
</p>

<h2>
	خلاصة
</h2>

<p>
	تعمل المُكرّرات والمولّدات العادية كما نريد لو لم تأخذ البيانات التي نحتاج وقتًا حتّى تتولّد.
</p>

<p>
	وحين نتوقّع بأنّ البيانات ستأتي بنحوٍ غير متزامن بينها انقطاعات، فيمكن استعمال البدائل غير المتزامنة مثل <code>for await..of</code> بدل <code>for..of</code>.
</p>

<p>
	الفرق في الصياغة بين المُكرّرات العادية وغير المتزامنة:
</p>

<table>
<thead><tr>
<th>
				 
			</th>
			<th>
				المتكرر
			</th>
			<th>
				المتكرر غير المتزامن
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				التابِع الذي يُقدّم المُكرّر
			</td>
			<td>
				<code>Symbol.iterator</code>
			</td>
			<td>
				<code>Symbol.asyncIterator</code>
			</td>
		</tr>
<tr>
<td>
				قيمة <code>next()‎</code> المُعادة هي
			</td>
			<td>
				<code>{value:…, done: true/false}</code>
			</td>
			<td>
				<code>Promise</code> والّتي تعوض لتصبح <code>{value:…, done: true/false}</code>
			</td>
		</tr>
</tbody>
</table>
<p>
	الفرق في الصياغة بين المولّدات العادية وغير المتزامنة:
</p>

<table>
<thead><tr>
<th>
				 
			</th>
			<th>
				المولدات
			</th>
			<th>
				المولدات غير المتزامنة
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				التعريف
			</td>
			<td>
				<code>function*</code>
			</td>
			<td>
				<code>async function*</code>
			</td>
		</tr>
<tr>
<td>
				<code>next()‎</code> القيمة المُعادة هي
			</td>
			<td>
				<code>{value:…, done: true/false}</code>
			</td>
			<td>
				<code>Promise</code> والّتي تعوض لتصبح <code>{value:…, done: true/false}</code>
			</td>
		</tr>
</tbody>
</table>
<p>
	غالبًا ونحن نعمل على تطوير الوِب نواجه سيولًا كبيرة من البيانات، سيولًا تأتينا قطعًا قطعًا (مثل تنزيل أو رفع الملفات الكبيرة).
</p>

<p>
	يمكننا هنا استعمال المولّدات غير المتزامنة لمُعالجة هذه البيانات. كما يجدر بنا الملاحظة كيف أنّ لبعض البيئات (مثل المتصفّحات) واجهات برمجة أخرى باسم السيول (Stream) وهي تقدّم لنا واجهات خاصّة للعمل مع السيول هذه، ذلك لتحويل شكل البيانات ونقلها من سيل إلى آخر (مثل التنزيل من مكان وإرسال ذلك التنزيل إلى مكان آخر مباشرةً).
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/async-iterators-generators" rel="external nofollow">Async iterators and generators</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
}

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
]]></description><guid isPermaLink="false">923</guid><pubDate>Fri, 19 Jun 2020 11:45:28 +0000</pubDate></item><item><title>&#x627;&#x644;&#x645;&#x648;&#x644;&#x62F;&#x627;&#x62A; Generators &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D9%88%D9%84%D8%AF%D8%A7%D8%AA-generators-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r922/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/78.jpg.6d8483465ea988be777ec8649136ed0a.jpg" /></p>
<p>
	تُعيد الدوالّ العادية قيمة واحدة فقط لا غير (أو لا تُعيد شيئًا).
</p>

<p>
	بينما يمكن للمولّدات إعادة (أو إنتاج yeild) أكثر من قيمة واحدةً بعد الأخرى حسب الطلب. تعمل المولّدات عملًا جميلًا جدًا مع <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%83%D8%B1%D9%91%D9%8E%D8%B1%D8%A9-iterables-r820/" rel="">الكائنات المكرَّرة (Iterables) في جافاسكربت</a> وتتيح لنا إنشاء سيول البيانات بسهولة بالغة.
</p>

<h2>
	الدوال المولدة
</h2>

<p>
	لإنشاء مولّد علينا استعمال صياغة مميّزة: <code>function*‎</code> أو ما يسمّونه ”الدالة المولِّدة“.
</p>

<p>
	هذا شكلها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_7" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  yield </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
  yield </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يختلف سلوك الدوال المولِّدة عن تلك العادية، فحين تُستدعى الدالة لا تُشغّل الشيفرة فيها، بل تُعيد كائنًا مميزًا نسمّيه ”كائن المولّد“ ليُدير عملية التنفيذ.
</p>

<p>
	خُذ نظرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_9" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  yield </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
  yield </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// تنشئ الدالة المولِّدة كائن مولّد</span><span class="pln">
let generator </span><span class="pun">=</span><span class="pln"> generateSequence</span><span class="pun">();</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">generator</span><span class="pun">);</span><span class="pln"> </span><span class="com">// [object Generator]</span></pre>

<p>
	الشيفرة الموجودة بداخل الدالّة لم تُنفذ بعد:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="47728" href="https://academy.hsoub.com/uploads/monthly_2020_06/generateSequence-1.png.5d8a4db55f1a528ec614a37c7e7a6962.png" rel="" data-fileext="png"><img alt="generateSequence-1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="47728" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_06/generateSequence-1.thumb.png.2c0b16c0f76eadeaa44731a4aa9b0924.png"></a>
</p>

<p>
	التابِع الأساسي للمولّد هو <code>next()‎</code>. متى استدعيناه بدأ عملية التنفيذ حتّى يصل أقرب جملة <code>yield &lt;value&gt;</code> (يمكن ألّا نكتب <code>value</code> وستصير القيمة <code>undefined</code>). بعدها يتوقّف تنفيذ الدالة مؤقّتًا وتُعاد القيمة <code>value</code> إلى الشيفرة الخارجية.
</p>

<p>
	ناتج التابِع <code>next()‎</code> لا يكون إلّا كائنًا له خاصيتين:
</p>

<ul>
	<li>
		<code>value</code>: القيمة التي أنتجها المولّد.
	</li>
	<li>
		<code>done</code>: القيمة <code>true</code> لو اكتملت شيفرة الدالة، وإلّا <code>false</code>.
	</li>
</ul>

<p>
	فمثلًا هنا نُنشِئ مولّدًا ونأخذ أوّل قيمة أنتجها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_11" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  yield </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
  yield </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let generator </span><span class="pun">=</span><span class="pln"> generateSequence</span><span class="pun">();</span><span class="pln">

let one </span><span class="pun">=</span><span class="pln"> generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">();</span><span class="pln"> </span><span class="com">// هنا</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">one</span><span class="pun">));</span><span class="pln"> </span><span class="com">// {value: 1, done: false}</span></pre>

<p>
	حاليًا أخذنا القيمة الأولى فقط، وسير تنفيذ الدالة موجود في السطر الثاني:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="47729" href="https://academy.hsoub.com/uploads/monthly_2020_06/generateSequence-2.png.4ab682c799aac3ef0eadbd296d3538d4.png" rel="" data-fileext="png"><img alt="generateSequence-2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="47729" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_06/generateSequence-2.thumb.png.26c3122e4849dfdfb5ecf6383e959d71.png"></a>
</p>

<p>
	فلنستدعِ <code>generator.next()‎</code> ثانيةً الآن. سنراه واصل تنفيذ الشيفرة وأعاد القيمة المُنتَجة <code>yield</code> التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_13" style=""><span class="pln">let two </span><span class="pun">=</span><span class="pln"> generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">();</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">two</span><span class="pun">));</span><span class="pln"> </span><span class="com">// {value: 2, done: false}</span></pre>

<p>
	 
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="47730" href="https://academy.hsoub.com/uploads/monthly_2020_06/generateSequence-3.png.936b19fdf309d80a687f163858260281.png" rel="" data-fileext="png"><img alt="generateSequence-3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="47730" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_06/generateSequence-3.thumb.png.01635a3fcd36ae2e0217ad9e3e67a5ca.png"></a>
</p>

<p>
	والآن إن شغّلناه مرّة ثالثة سيصل سير التنفيذ إلى عبارة <code>return</code> ويُنهي الدالة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_15" style=""><span class="pln">let three </span><span class="pun">=</span><span class="pln"> generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">();</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">three</span><span class="pun">));</span><span class="pln"> </span><span class="com">// {value: 3, done: true} // ‫لاحِظ قيمة done</span></pre>

<p>
	 
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="47731" href="https://academy.hsoub.com/uploads/monthly_2020_06/generateSequence-4.png.5420f7ba5a876a0db0a0feaac0cf0e29.png" rel="" data-fileext="png"><img alt="generateSequence-4.png" class="ipsImage ipsImage_thumbnailed" data-fileid="47731" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_06/generateSequence-4.thumb.png.995a1ca65aaf097dd3cc18716355acb7.png"></a>
</p>

<p>
	الآن اكتمل المولّد بقيمة <code>value:3</code> ويمكننا الآن معالجتها. عرفنا ذلك من <code>done:true</code>.
</p>

<p>
	الاستدعاءات اللاحقة على <code>generator.next()‎</code> لن تكون منطقية الآن. ولو حصلت فستُعيد الدالة الكائن نفسه: <code>{done: true}</code>.
</p>

<p>
	<strong>ملاحظة</strong>: الصياغة <code>function* f(…)‎</code> أم <code>function *f(…)‎</code>؟ في الحقيقة كِلاهما صحيح. ولكن عادةً تكون الصياغة الأولى مفضلة أكثر من الثانية. وتشير النجمة <code>*</code> على أنها دالة مولّد، إذ تصف النوع وليس الاسم، لذلك يجب أن ندمج الكلمة المفتاحية <code>function</code> بالنجمة.
</p>

<h2>
	المولدات قابلة للتكرار
</h2>

<p>
	نفترض أنّك توقّعت ذلك حين رأيت التابِع <code>next()‎</code>، إذ أن المولدات قابلة للتكرار iterable، فيمكننا المرور على عناصره عبر <code>for..of</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_17" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  yield </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
  yield </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let generator </span><span class="pun">=</span><span class="pln"> generateSequence</span><span class="pun">();</span><span class="pln">

</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let value of generator</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‏1 ثمّ 2</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هذا أجمل من استدعاء <code>‎.next().value</code>، أم لا؟
</p>

<p>
	ولكن… لاحِظ: يعرض المثال أعلاه <code>1</code> ثمّ <code>2</code> وفقط. لن يعرض <code>3</code> مطلقًا!
</p>

<p>
	هذا لأنّ عملية التكرار لـِ <code>for..of</code> تتجاهل قيمة <code>value</code> الأخيرة حين تكون <code>done: true</code>. لذا لو أردنا أن تظهر النتائج كلّها لعملية تكرار <code>for..of</code>، فعلينا إعادتها باستعمال <code>yield</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_19" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  yield </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
  yield </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
  yield </span><span class="lit">3</span><span class="pun">;</span><span class="pln"> </span><span class="com">// هكذا</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let generator </span><span class="pun">=</span><span class="pln"> generateSequence</span><span class="pun">();</span><span class="pln">

</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let value of generator</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‏1 ثمّ 2 ثمّ 3</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نظرًا من كون المولّدات قابلة للتكرار، يمكننا استدعاء جميع الدوالّ المتعلّقة بذلك، مثل: معامل «البقية» <code>...</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_21" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  yield </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
  yield </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
  yield </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let sequence </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">generateSequence</span><span class="pun">()];</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">sequence</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 0, 1, 2, 3</span></pre>

<p>
	يحول التابع <code>‎...generateSequence()‎</code> في الشيفرة أعلاه كائن المولد القابل للتكرار إلى مصفوفة من العناصر (لمزيد من المعلومات أحيلك إلى هذا المقال "المُعاملات «البقية» ومُعامل التوزيع").
</p>

<h2>
	استعمال المولدات على أنها مكرّرات
</h2>

<p>
	سابقًا في فصل "<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%85%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D9%82%D9%8A%D8%A9-%D9%88%D9%85%D8%B9%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D8%AA%D9%88%D8%B2%D9%8A%D8%B9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r871/" rel="">المُعاملات «البقية» ومُعامل التوزيع</a>" أنشأنا كائن <code>range</code> يمكن تكراره والذي يعيد القيم بين قيميتين <code>from..to</code>.
</p>

<p>
	لنتذكّر الشيفرة معًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_23" style=""><span class="pln">let range </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  from</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  to</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
  </span><span class="com">// ‫تستدعي حلقة for..of هذه الدالّة مرة واحدة في البداية</span><span class="pln">
  </span><span class="pun">[</span><span class="typ">Symbol</span><span class="pun">.</span><span class="pln">iterator</span><span class="pun">]()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ‫...ستعيد كائن مُكرُر</span><span class="pln">
    </span><span class="com">// ‫لاحقًا ستعمل حلقة for..of مع ذلك الكائن وتطلب منه القيم التالية</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      current</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">from</span><span class="pun">,</span><span class="pln">
      last</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">to</span><span class="pun">,</span><span class="pln">
      </span><span class="com">// ‫تستدعى next()‎ في كلّ تكرار من خلال الحلقة for..of</span><span class="pln">
      next</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// ‫يجب أن تعيد القيم ككائن {done:.., value :...}</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">current </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">last</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> done</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">current</span><span class="pun">++</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> done</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="com">// ‫عملية التكرار تمرُّ عبر range من range.from إلى range.to</span><span class="pln">
alert</span><span class="pun">([...</span><span class="pln">range</span><span class="pun">]);</span><span class="pln"> </span><span class="com">// 1,2,3,4,5</span></pre>

<p>
	يمكننا استعمال دالة المولِّدة كمكرّرات من خلال <code>Symbol.iterator</code>.
</p>

<p>
	إليك نفس الكائن <code>range</code>, ولكن بطريقة أكثر إيجازًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_25" style=""><span class="pln">let range </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  from</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  to</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">

  </span><span class="pun">*[</span><span class="typ">Symbol</span><span class="pun">.</span><span class="pln">iterator</span><span class="pun">]()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// ‫اختصارًا لـِ ‎[Symbol.iterator]: function*‎()‎</span><span class="pln">
    </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let value </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">from</span><span class="pun">;</span><span class="pln"> value </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">to</span><span class="pun">;</span><span class="pln"> value</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      yield value</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> </span><span class="pun">[...</span><span class="pln">range</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1,2,3,4,5</span></pre>

<p>
	الشيفرة تعمل إذ يُعيد <code>range[Symbol.iterator]()‎</code> الآن مولّدًا، وما تتوقّعه <code>for..of</code> هي توابِع تلك المولّدات بعينها:
</p>

<ul>
	<li>
		إذ لها التابِع <code>‎.next()‎</code>
	</li>
	<li>
		وتُعيد القيم على النحو الآتي <code>{value: ..., done: true/false}</code>
	</li>
</ul>

<p>
	بالطبع هذه ليست مصادفة أضيفت المولّدات إلى لغة جافاسكربت مع الأخذ بعين الاعتبار للمكرّرات لتنفيذهم بسهولة.
</p>

<p>
	إن التنوع في المولدات أعطى شيفرة موجزة أكثر الشيفرة الأصلية لكائن <code>range</code>، وجميعهم لديهم نفس الخصائص الوظيفية.
</p>

<p>
	<strong>ملاحظة</strong>: المولّدات يمكن أن تولد قيمًا للأبد في الأمثلة أعلاه أنشأنا متتالية منتهية من القيم، ولكن باستخدام المولد نستطيع أن ننتج قيمًا إلى الأبد. على سبيل المثال لننشئ متتالية غير منتهية من الأرقام العشوائية الزائفة.
</p>

<p>
	ومن المؤكد أن هذا المولّد سيحتاجُ إلى طريقة لإيقافه مثل: <code>break</code> (أو <code>return</code>) في حلقة <code>for..of</code>. وإلا فإن الحلقة ستستمر إلى الأبد.
</p>

<h2>
	تراكب المولّدات
</h2>

<p>
	تراكب المولّدات (Generator composition) هي ميزة خاصّة للمولّدات تتيح لها ”تضمين“ المولّدات الأخرى فيها دون عناء.
</p>

<p>
	فمثلًا لدينا هذه الدالة التي تُولّد سلسلة من الأعداد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_27" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">(</span><span class="pln">start</span><span class="pun">,</span><span class="pln"> end</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> end</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> yield i</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نريد الآن إعادة استعمالها لتوليد سلسلة أعداد معقّدة أكثر من هذه أعلاه:
</p>

<ul>
	<li>
		أولًا الأرقام <code>0..9</code> (ورموز المحارف اليونيكوديّة هو من 48 إلى 57)
	</li>
	<li>
		ثمّ الأحرف الأبجدية الإنجليزية بالحالة الكبيرة <code>A..Z</code> (ورموزها من 65 إلى 90)
	</li>
	<li>
		ثمّ الأحرف الأبجدية الإنجليزية بالحالة الصغيرة <code>a..z</code> (ورموزها من 97 إلى 122)
	</li>
</ul>

<p>
	يمكننا استعمال هذه السلسلة لإنشاء كلمات السرّ باختيار المحارف منها مثلًا (ويمكننا إضافة المحارف الخاصّة أيضًا). ولكن لذلك علينا توليدها أولًا.
</p>

<p>
	علينا في الدوالّ العادية (لتضمين النواتج من دوالّ أخرى) استدعاء تلك الدوالّ وتخزين نواتجها ومن ثمّ ربطها في نهاية الدالة الأمّ.
</p>

<p>
	وفي المولّدات نستعمل الصياغة المميّزة <code>yield*</code> لتضمين (أو تركيب) مولّدين داخل بعض.
</p>

<p>
	المولّد المركّب:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_29" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">(</span><span class="pln">start</span><span class="pun">,</span><span class="pln"> end</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> end</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> yield i</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pun">*</span><span class="pln"> generatePasswordCodes</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="com">// 0..9</span><span class="pln">
  yield</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">(</span><span class="lit">48</span><span class="pun">,</span><span class="pln"> </span><span class="lit">57</span><span class="pun">);</span><span class="pln">

  </span><span class="com">// A..Z</span><span class="pln">
  yield</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">(</span><span class="lit">65</span><span class="pun">,</span><span class="pln"> </span><span class="lit">90</span><span class="pun">);</span><span class="pln">

  </span><span class="com">// a..z</span><span class="pln">
  yield</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">(</span><span class="lit">97</span><span class="pun">,</span><span class="pln"> </span><span class="lit">122</span><span class="pun">);</span><span class="pln">

</span><span class="pun">}</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let code of generatePasswordCodes</span><span class="pun">())</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  str </span><span class="pun">+=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">.</span><span class="pln">fromCharCode</span><span class="pun">(</span><span class="pln">code</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">str</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 0..9A..Za..z</span></pre>

<p>
	توجه <code>yield*‎</code> التنفيذ إلى مولّد آخر. مما يعني أن <code>yield* gen</code> ستكرر عبر المولّد <code>gen</code> وستُعيد نتائجه للخارج. أي كما او أنتجت هذه القيم بمولّد خارجي.
</p>

<p>
	النتيجة نفسها كما لو أننا ضمنَا الشيفرة من المولّدات المتداخلة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_31" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> generateSequence</span><span class="pun">(</span><span class="pln">start</span><span class="pun">,</span><span class="pln"> end</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> end</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> yield i</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pun">*</span><span class="pln"> generateAlphaNum</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">


  </span><span class="com">// yield* generateSequence(48, 57);</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">48</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">57</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> yield i</span><span class="pun">;</span><span class="pln">

  </span><span class="com">// yield* generateSequence(65, 90);</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">65</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">90</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> yield i</span><span class="pun">;</span><span class="pln">

  </span><span class="com">// yield* generateSequence(97, 122);</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">97</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">122</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> yield i</span><span class="pun">;</span><span class="pln">


</span><span class="pun">}</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let code of generateAlphaNum</span><span class="pun">())</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  str </span><span class="pun">+=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">.</span><span class="pln">fromCharCode</span><span class="pun">(</span><span class="pln">code</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">str</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 0..9A..Za..z</span></pre>

<p>
	تراكب المولّدات هو فعلًا طريقة بديهيّة لإدخال أكثر من مولّد في مولّد واحد، ولا تستعمل ذاكرة إضافية لتخزين النواتج البينيّة.
</p>

<h2>
	عبارة ”yield“ باتجاهين اثنين
</h2>

<p>
	إلى هنا نرى المولّدات تشبه كائنات المكرّرات كثيرًا، فقط أنّ لها صياغة مميّزة لتوليد القيم. ولكن وعلى أرض الواقع، المولّدات أكثر مرونة وفاعليّة.
</p>

<p>
	إذ أنّ <code>yield</code> تعمل بالمجيء وبالإياب: فلا تُعيد الناتج إلى الخارج فحسب بل أيضًا يمكنها تمرير القيمة إلى داخل المولّد.
</p>

<p>
	لذلك علينا استدعاء <code>generator.next(arg)‎</code> بتمرير وسيط واحد. هذا الوسيط سيكون ناتج <code>yield</code>.
</p>

<p>
	الأفضل لو نرى مثالًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_33" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> gen</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// نمرّر سؤالًا إلى الشيفرة الخارجية وننتظر إجابةً عليه</span><span class="pln">
  let result </span><span class="pun">=</span><span class="pln"> yield </span><span class="str">"2 + 2 = ?"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let generator </span><span class="pun">=</span><span class="pln"> gen</span><span class="pun">();</span><span class="pln">

let question </span><span class="pun">=</span><span class="pln"> generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">().</span><span class="pln">value</span><span class="pun">;</span><span class="pln"> </span><span class="com">// &lt;-- تُعيد yield القيمة</span><span class="pln">

generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">(</span><span class="lit">4</span><span class="pun">);</span><span class="pln"> </span><span class="com">// --&gt; نمرّر القيمة إلى المولّد  </span></pre>

<p>
	 
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="47733" href="https://academy.hsoub.com/uploads/monthly_2020_06/genYield2.png.6a08d0e2f4b5355fbd1b6ce7579660e9.png" rel="" data-fileext="png"><img alt="genYield2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="47733" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_06/genYield2.thumb.png.f2adf1b16a2286cdbbaa3446d77a2312.png"></a>
</p>

<ol>
	<li>
		يكون الاستدعاء الأوّل للتابِع <code>generator.next()‎</code> دومًا <strong>دون</strong> تمرير أيّ وسيط. يبدأ الاستدعاء التنفيذَ ويُعيد ناتج أوّل <code>yield "2+2=?‎"</code>. هنا يُوقف المولّد التنفيذ مؤقّتًا (على ذلك السطر).
	</li>
	<li>
		ثمّ (كما نرى في الصورة) يُوضع ناتج <code>yield</code> في متغير السؤال <code>question</code> في الشيفرة التي استدعت المولّد.
	</li>
	<li>
		وعند <code>generator.next(4)‎</code> يُواصل المولّد عمله ويستلم <code>4</code> ناتجًا: <code>let result = 4</code>.
	</li>
</ol>

<p>
	لاحِظ أنّه ليس على الشيفرة الخارجية استدعاء <code>next(4)‎</code> مباشرةً وفي الحال، بل يمكن أن تأخذ الوقت الذي تريد. سيبقى المولّد منتظرًا ولن تكون مشكلة.
</p>

<p>
	مثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_35" style=""><span class="com">// نُواصل عمل المولّد بعد زمن معيّن</span><span class="pln">
setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">(</span><span class="lit">4</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span></pre>

<p>
	كما نرى فعلى العكس تمامًا من الدوال العادية، يمكن للمولّد ولشيفرة الاستدعاء تبادل النتائج بتمرير القيم إلى <code>next/yield</code>.
</p>

<p>
	ليتوضّح هذا أكثر سنرى مثالًا آخر فيه استدعاءات أكثر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_37" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> gen</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let ask1 </span><span class="pun">=</span><span class="pln"> yield </span><span class="str">"2 + 2 = ?"</span><span class="pun">;</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln">ask1</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 4</span><span class="pln">

  let ask2 </span><span class="pun">=</span><span class="pln"> yield </span><span class="str">"3 * 3 = ?"</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln">ask2</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 9</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let generator </span><span class="pun">=</span><span class="pln"> gen</span><span class="pun">();</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">().</span><span class="pln">value </span><span class="pun">);</span><span class="pln"> </span><span class="com">// "2 + 2 = ?"</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">(</span><span class="lit">4</span><span class="pun">).</span><span class="pln">value </span><span class="pun">);</span><span class="pln"> </span><span class="com">// "3 * 3 = ?"</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">(</span><span class="lit">9</span><span class="pun">).</span><span class="pln">done </span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span></pre>

<p>
	إليك صورة سير التنفيذ:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="47732" href="https://academy.hsoub.com/uploads/monthly_2020_06/genYield2-2.png.b49e6228da4590ba42015e0cd973e836.png" rel="" data-fileext="png"><img alt="genYield2-2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="47732" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_06/genYield2-2.thumb.png.721c89a1bf43963aed5743b4a492bee3.png"></a>
</p>

<ol>
	<li>
		استدعاء <code>.next()‎</code> الأوّل يبدأ التنفيذ، ويصل إلى أوّل عبارة <code>yield</code>.
	</li>
	<li>
		يُعاد الناتج إلى الشيفرة الخارجية.
	</li>
	<li>
		يمرّر استدعاء <code>.next(4)‎</code> الثاني القيمة <code>4</code> إلى المولّد ثانيةً على أنّها ناتج أوّل مُنتَج <code>yield</code>، ويُواصل التنفيذ.
	</li>
	<li>
		يصل التنفيذ إلى عبارة <code>yield</code> الثانية، وتصير هي ناتج الاستدعاء.
	</li>
	<li>
		يُمرّر <code>next(9)‎</code> الثالث القيمة <code>9</code> إلى المولّد على أنّها ناتج ثاني مُنتَج <code>yield</code> ويُواصل التنفيذ حتّى يصل نهاية الدالة، بذلك تكون <code>done: true</code>.
	</li>
</ol>

<p>
	تشبه هذه لعبة تنس الطاولة، حيث يمرّر كلّ تابِع <code>next(value)‎</code> (باستثناء الأوّل طبعًا) القيمة إلى المولّد فتصير ناتج المُنتَج <code>yield</code> الحالي، ومن ثمّ
</p>

<h2>
	generator.throw
</h2>

<p>
	يمكن للشيفرات الخارجية تمرير القيم إلى المولّدات على أنّها نواتج <code>yield</code> (كما لاحظنا من الأمثلة أعلاه).
</p>

<p>
	ويمكنها أيضًا بدء (أو رمي) خطأ أيضًا. هذا طبيعي إذ الأخطاء هي نواتج، نوعًا ما.
</p>

<p>
	علينا استدعاء التابِع <code>generator.throw(err)‎</code> لتمرير الأخطاء إلى عبارة <code>yield</code>. في هذه الحال يُرمى الخطأ <code>err</code> عند السطر الذي فيه <code>yield</code>.
</p>

<p>
	فمثلًا تؤدّي عبارة <code>"‎2 + 2 = ?‎"</code> هنا إلى خطأ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_39" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> gen</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let result </span><span class="pun">=</span><span class="pln"> yield </span><span class="str">"2 + 2 = ?"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// (1)</span><span class="pln">

    alert</span><span class="pun">(</span><span class="str">"The execution does not reach here, because the exception is thrown above"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">e</span><span class="pun">);</span><span class="pln"> </span><span class="com">// أظهر الخطأ</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let generator </span><span class="pun">=</span><span class="pln"> gen</span><span class="pun">();</span><span class="pln">

let question </span><span class="pun">=</span><span class="pln"> generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">().</span><span class="pln">value</span><span class="pun">;</span><span class="pln">


generator</span><span class="pun">.</span><span class="kwd">throw</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"The answer is not found in my database"</span><span class="pun">));</span><span class="pln"> </span><span class="com">// (2)</span></pre>

<p>
	الخطأ الذي رميَ في المولد عند السطر <code>(2)</code> يقودنا إلى الخطأ في السطر <code>(1)</code> مع <code>yield</code>. في المثال أعلاه التقطت <code>try..catch</code> الخطأ وثمّ عرضته.
</p>

<p>
	لو لم نلتقطه سيكون مآله (مآل أيّ استثناء آخر غيره) أن ”يسقط“ من المولّد إلى الشيفرة التي استدعت المولّد.
</p>

<p>
	هل يمكننا التقاط الخطأ في سطر شيفرة الاستدعاء الّتي تحتوي على <code>generator.throw</code>، (المشار إليه <code>(2)</code>)، هكذا؟
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_41" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> generate</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let result </span><span class="pun">=</span><span class="pln"> yield </span><span class="str">"2 + 2 = ?"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ في هذا السطر</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let generator </span><span class="pun">=</span><span class="pln"> generate</span><span class="pun">();</span><span class="pln">

let question </span><span class="pun">=</span><span class="pln"> generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">().</span><span class="pln">value</span><span class="pun">;</span><span class="pln">


</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  generator</span><span class="pun">.</span><span class="kwd">throw</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"The answer is not found in my database"</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">e</span><span class="pun">);</span><span class="pln"> </span><span class="com">// عرض الخطأ</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إن لم نلتقط الخطأ هنا فعندئذ وكما هي العادة ستنهار الشيفرة الخارجية (إن كانت موجودة) وإذا لم يكتشف الخطأ أيضًا عندها سينهار السكربت بالكامل.
</p>

<h2>
	خلاصة
</h2>

<ul>
	<li>
		تُنشِئ الدوال المولِّدة <code>function* f(…) {…}‎</code> المولّدات.
	</li>
	<li>
		يوجد المُعامل <code>yield</code> داخل المولدات (فقط).
	</li>
	<li>
		تتبادل الشيفرة الخارجية مع المولدات النتائج من خلال استدعاءات <code>next/yield</code>.
	</li>
</ul>

<p>
	نادرًا ما تستخدم المولّدات في الإصدار الحديث من جافاسكربت. لكن في بعض الأحيان نستخدمها لتبادل البيانات بين الشيفرة المستدعاة أثناء تنفيذ شيفرة وحيدة. وبالتأكيد إنها رائعة لتوليد أشياء قابلة للتكرار.
</p>

<p>
	في الفصل التالي، سنتعرف على مولدات غير متزامنة، والّتي تُستخدم لقراءة تدفقات البيانات غير المتزامنة (على سبيل المثال ، نرى على الإنترنت خدمات كثيرة تقدّم لنا البيانات على صفحات [paginated]) في حلقات <code>for await ... of</code>.
</p>

<p>
	في برمجة الوِب، غالبًا ما نعمل مع البيانات المتدفقة، لذا فهذه حالة استخدام أخرى مهمة جدًا.
</p>

<h2>
	تمارين
</h2>

<h3>
	مولد أرقام شبه عشوائية
</h3>

<p>
	نواجه كثيرًا من الأحيان حاجة ماسّة إلى بيانات عشوائية.
</p>

<p>
	إحدى هذه الأحيان هي إجراء الاختبارات، فنحتاج إلى بيانات عشوائية كانت نصوص أو أعداد أو أيّ شيء آخر لاختبار الشيفرات والبنى البرمجية.
</p>

<p>
	يمكننا في جافاسكربت استعمال <code>Math.random()‎</code>، ولكن لو حدث خطب ما فنودّ إعادة إجراء الاختبار باستعمال نفس البيانات بالضبط (كي نختبر هذه البيانات).
</p>

<p>
	لهذا الغرض نستعمل ما يسمّى ”بمولّدات الأعداد شبه العشوائية المزروعة“. تأخذ هذه المولّدات "البذرة“ والقيمة الأولى ومن ثمّ تولّد القيم اللاحقة باستعمال معادلة رياضية بحيث أنّ كلّ بذرة تُنتج نفس السلسلة دائمًا، وهكذا يمكننا ببساطة استنساخ التدفق بالكامل من خلال بذورها فقط.
</p>

<p>
	إليك مثال عن هذه المعادلة التي تولّد قيمًا موزّعة توزيعًا
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_43" style=""><span class="pln">next </span><span class="pun">=</span><span class="pln"> previous </span><span class="pun">*</span><span class="pln"> </span><span class="lit">16807</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> </span><span class="lit">2147483647</span></pre>

<p>
	لو استعملنا <code>1</code> …، فستكون القيم كالآتي:
</p>

<ol>
	<li>
		<code>16807</code>
	</li>
	<li>
		<code>282475249</code>
	</li>
	<li>
		<code>1622650073</code>
	</li>
	<li>
		…وهكذا…
	</li>
</ol>

<p>
	مهمّة هذا التمرين هو إنشاء الدالة المولِّدة <code>pseudoRandom(seed)‎</code> فتأخذ البذرة <code>seed</code> وتُنشِئ مولّدًا بالمعادلة أعلاه.
</p>

<p>
	طريقة الاستعمال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_45" style=""><span class="pln">let generator </span><span class="pun">=</span><span class="pln"> pseudoRandom</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">().</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 16807</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">().</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 282475249</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">().</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1622650073</span></pre>

<h4>
	الحل
</h4>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_47" style=""><span class="kwd">function</span><span class="pun">*</span><span class="pln"> pseudoRandom</span><span class="pun">(</span><span class="pln">seed</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let value </span><span class="pun">=</span><span class="pln"> seed</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">while</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    value </span><span class="pun">=</span><span class="pln"> value </span><span class="pun">*</span><span class="pln"> </span><span class="lit">16807</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> </span><span class="lit">2147483647</span><span class="pln">
    yield value</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">};</span><span class="pln">

let generator </span><span class="pun">=</span><span class="pln"> pseudoRandom</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">().</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 16807</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">().</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 282475249</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">generator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">().</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1622650073</span></pre>

<p>
	لاحِظ أننا نستطيع تأدية ذات الأمر بالدوال العادية هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5861_49" style=""><span class="kwd">function</span><span class="pln"> pseudoRandom</span><span class="pun">(</span><span class="pln">seed</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let value </span><span class="pun">=</span><span class="pln"> seed</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    value </span><span class="pun">=</span><span class="pln"> value </span><span class="pun">*</span><span class="pln"> </span><span class="lit">16807</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> </span><span class="lit">2147483647</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let generator </span><span class="pun">=</span><span class="pln"> pseudoRandom</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">generator</span><span class="pun">());</span><span class="pln"> </span><span class="com">// 16807</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">generator</span><span class="pun">());</span><span class="pln"> </span><span class="com">// 282475249</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">generator</span><span class="pun">());</span><span class="pln"> </span><span class="com">// 1622650073</span></pre>

<p>
	ستعمل هذه أيضًا. ولكن بعد ذلك سنفقدُ قابلية التكرار باستخدام <code>for..of</code> واستخدام المولّد المركب، وتلك ممكن أن تكون مفيدة في مكان آخر.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/generators" rel="external nofollow">Generators</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">922</guid><pubDate>Thu, 06 Aug 2020 18:01:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x644;&#x627;&#x62A;&#x632;&#x627;&#x645;&#x646; &#x648;&#x627;&#x644;&#x627;&#x646;&#x62A;&#x638;&#x627;&#x631; async/await &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%84%D8%A7%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%88%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D8%B8%D8%A7%D8%B1-asyncawait-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r921/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/77.jpg.cfa5011a1e49329b321d728593cc78bc.jpg" /></p>

<p>
	توجد صياغة مميّزة للعمل مع <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promise-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r915/" rel="">الوعود</a> بنحوٍ أكثر سهولة تُدعى <code>async/await</code>. فهمها أسهل من شرب الماء واستعمالها
</p>

<h2>
	الدوال غير المتزامنة
</h2>

<p>
	فلنبدأ أولًا بكلمة <code>async</code> المفتاحية. يمكننا وضعها قبل الدوال هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_7" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وجود الكلمة ”async“ قبل (اختصار ”غير متزامنة“ بالإنجليزية) يعني أمرًا واحدًا: تُعيد الدالة وعدًا دومًا.
</p>

<p>
	فمثلًا تُعيد هذه الدالة وعدًا مُنجز فيه ناتج <code>1</code>. فلنرى:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_10" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

f</span><span class="pun">().</span><span class="pln">then</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span></pre>

<p>
	كما يمكننا أيضًا إعادة وعد صراحةً:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_12" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

f</span><span class="pun">().</span><span class="pln">then</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span></pre>

<p>
	هكذا تضمن لنا <code>async</code> بأنّ الدالة ستُعيد وعدًا وستُغلّف الأشياء التي ليست بوعود وعودًا. بسيطة صح؟ ليس هذا فحسب، بل هناك أيضًا الكلمة المفتاحية <code>await</code> التي تعمل فقط في الدوال غير المتزامنة <code>async</code>، وهي الأخرى جميلة.
</p>

<h2>
	Await
</h2>

<p>
	الصياغة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_15" style="">
<span class="com">// لا تعمل إلّا في الدوال غير المتزامنة</span><span class="pln">
let value </span><span class="pun">=</span><span class="pln"> await promise</span><span class="pun">;</span></pre>

<p>
	الكلمة المفتاحية تجعل لغة جافاسكربت تنتظر حتى يُنجز الوعد ويعيد نتيجة.
</p>

<p>
	إليك مثالًا لوعد نُفذّ خلال ثانية واحدة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_17" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="str">"done!"</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">)</span><span class="pln"> </span><span class="com">// تم!‏</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  let result </span><span class="pun">=</span><span class="pln"> await promise</span><span class="pun">;</span><span class="pln"> </span><span class="com">// ننتظر ... الوعد (*)</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln"> </span><span class="com">// "done!" تم!‏</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

f</span><span class="pun">();</span></pre>

<p>
	يتوقّف تنفيذ الدالة ”قليلًا“ عند السطر <code>(*)</code> ويتواصل متى ما أُنجز الوعد وصار <code>result</code> ناتجه. الشيفرة أعلاه تعرض ”تم!“ بعد ثانية واحدة.
</p>

<p>
	لنوضح أمرًا مهمًا: تجعل <code>await</code> لغة جافاسكربت تنتظر حتى يُنجز الوعد، وبعدها تذهب مع النتيجة. هذا لن يكلفنا أي موارد من المعالج لأن المحرك مُنشغل بمهام أخرى في الوقت نفسه، مثل: تنفيذ سكربتات أُخرى، التعامل مع الأحداث ..إلخ.
</p>

<p>
	هي مجرد صياغة أنيقة أكثر من صياغة <code>promise.then</code> للحصول على ناتج الوعد. كما أنها أسهل للقراءة والكتابة..
</p>

<p>
	<strong>تحذير</strong>: لا يمكننا استخدام <code>await</code> في الدوال العادية إذا حاولنا استخدام الكلمة المفتاحية <code>await</code> في الدوال العادية فسيظهر خطأ في في الصياغة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_19" style="">
<span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let promise </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
  let result </span><span class="pun">=</span><span class="pln"> await promise</span><span class="pun">;</span><span class="pln"> </span><span class="com">// Syntax error</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنحصل على هذا الخطأ إذا لم نضع <code>async</code> قبل الدالّة، كما قلنا <code>await</code> تعمل فقط في الدوالّ غير المتزامنة.
</p>

<p>
	لنأخذ مثال <code>showAvatar()‎</code> من الفصل <a href="https://academy.hsoub.com/programming/javascript/%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promises-chaining-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r916/" rel="">سَلسلة الوعود</a> ونُعيد كتابته باستعمال <code>async/await</code>:
</p>

<ol>
<li>
		علينا استبدال استدعاءات <code>‎.then</code> ووضع <code>await</code>.
	</li>
	<li>
		علينا أيضًا تحويل الدالة لتكون غير متزامنة <code>async</code> كي تعمل <code>await</code>.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_21" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> showAvatar</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ‫إقرأ ملفات JSON</span><span class="pln">
  let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'/article/promise-chaining/user.json'</span><span class="pun">);</span><span class="pln">
  let user </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">

  </span><span class="com">// ‫إقرأ مستخدم github</span><span class="pln">
  let githubResponse </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(`</span><span class="pln">https</span><span class="pun">:</span><span class="com">//api.github.com/users/${user.name}`);</span><span class="pln">
  let githubUser </span><span class="pun">=</span><span class="pln"> await githubResponse</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">

  </span><span class="com">// ‫أظهرالصورة الرمزية avatar</span><span class="pln">
  let img </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'img'</span><span class="pun">);</span><span class="pln">
  img</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> githubUser</span><span class="pun">.</span><span class="pln">avatar_url</span><span class="pun">;</span><span class="pln">
  img</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"promise-avatar-example"</span><span class="pun">;</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">img</span><span class="pun">);</span><span class="pln">

  </span><span class="com">// انتظر  3 ثواني</span><span class="pln">
  await </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">));</span><span class="pln">

  img</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">();</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> githubUser</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

showAvatar</span><span class="pun">();</span></pre>

<p>
	شيفرة نظيفة وسهلة القراءة! أفضل من السابقة بأميال.
</p>

<p>
	<strong>ملاحظة</strong>: لن تعمل <code>await</code> في الشيفرة ذات المستوى الأعلى يميل المبتدئين إلى نسيان أن <code>await</code> لن تعمل في الشيفرة البرمجية ذات هرمية أعلى(ذات المستوى الأعلى). فمثلًا لن يعمل هذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_23" style="">
<span class="com">// خطأ صياغيّ في الشيفرة عالية المستوى</span><span class="pln">
let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'/article/promise-chaining/user.json'</span><span class="pun">);</span><span class="pln">
let user </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span></pre>

<p>
	يمكننا تغليفها بداخل دالّة متزامنة مجهولة، هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_25" style="">
<span class="pun">(</span><span class="pln">async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'/article/promise-chaining/user.json'</span><span class="pun">);</span><span class="pln">
  let user </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
</span><span class="pun">})();</span></pre>

<p>
	<strong>ملاحظة</strong>: إن <code>await</code> تقبل "thenables" تسمح <code>await</code> باستخدام كائنات thenable (تلك التي تستخدم دالة <code>then</code> القابلة للاستدعاء) تمامًا مثل <code>promise.then</code>. الفكرة أنه يمكن ألا يكون الكائن الخارجي وعدًا ولكنه متوافق مع الوعد: إن كان يدعم <code>‎.then</code>، وهذا يكفي لاستخدامه مع <code>await</code>.
</p>

<p>
	هنا مثال لاستخدام صنف <code>Thenable</code> والكلمة المفتاحية <code>await</code> أدناه ستقبلُ حالاتها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_27" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Thenable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">num</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">num </span><span class="pun">=</span><span class="pln"> num</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  then</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">);</span><span class="pln">
    </span><span class="com">// ‫نفذ عملية this.num*2 بعد ثانية واحدة</span><span class="pln">
    setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">num </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

async </span><span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// انتظر لثانية واحدة. ثم النتيجة ستصبح 2 </span><span class="pln">
  let result </span><span class="pun">=</span><span class="pln"> await </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Thenable</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

f</span><span class="pun">();</span></pre>

<p>
	إذا حصَلت <code>await</code> على كائن ليس وعدًا مع <code>‎.then</code> فإنه يستدعي الدوالّ المضمنة في اللغة مثل: <code>resolve</code> و <code>reject</code> كوسطاء (كما يفعل المنفذّ للوعد العادي تمامًا). وثم تنتظر <code>await</code> حتى يستدعى أحدهم (في المثال أعلاه في السطر <code>(*)</code>) ثم يواصل مع النتيجة.
</p>

<p>
	<strong>ملاحظة</strong>: لتعريف دالّة صنف غير متزامنة إضفها مع الكلمة <code>async</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_29" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Waiter</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  async wait</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> await </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Waiter</span><span class="pun">()</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">wait</span><span class="pun">()</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span></pre>

<p>
	لاحظ أن المعنى هو نفسه: فهو يضمن أن القيمة المرتجعة هي وعد و<code>await</code> مفعّلة أيضًا.
</p>

<h2>
	التعامل مع الأخطاء
</h2>

<p>
	لو نُفذّ الوعد بنجاح فسيُعيد <code>await promise</code> الناتج، ولكن لو حصلت حالة رفض فسترمي الخطأ كما لو كانت إفادة <code>throw</code> مكتوبة.
</p>

<p>
	إليك الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_31" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  await </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">));</span><span class="pln"> </span><span class="com">// لاحظ</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	و… هي ذاتها هذه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_33" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// هنا</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في الواقع تأخذ الوعود وقتًا قبل أن تُرفض. في تلك لحالة فستكون هناك مهلة قبل أن يرمي <code>await</code> الخطأ.
</p>

<p>
	يمكننا التقاط ذاك الخطأ باستعمال <code>try..catch</code> كما استعملنا <code>throw</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_35" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'http://no-such-url'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln"> </span><span class="com">// TypeError: failed to fetch خطأ في النوع: فشل الجلب</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

f</span><span class="pun">();</span></pre>

<p>
	لو حدث خطأ فينتقل سير التنفيذ إلى كتلة <code>catch</code>. يمكننا أيضًا وضع أكثر من سطر واحد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_37" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'/no-user-here'</span><span class="pun">);</span><span class="pln">
    let user </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ‫تلتقط أخطاء fetch وresponse.json معًا</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

f</span><span class="pun">();</span></pre>

<p>
	لو لم نضع <code>try..catch</code> فستكون حالة الوعد الذي نفّذه استدعاء الدالة غير المتزامنة <code>f()‎</code> - يكون بحالة رفض. يمكننا هنا استعمال <code>‎.catch</code> للتعامل معه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_39" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'http://no-such-url'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// ‫يصير استدعاء f()‎ وعدًا مرفوضًا</span><span class="pln">
f</span><span class="pun">().</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// TypeError: failed to fetch خطأ في النوع: فشل الجلب (*)</span></pre>

<p>
	لو نسينا هنا إضافة <code>‎.catch</code> فسنتلقّى خطأ وعود لم نتعامل معه (يمكن أن نراه من الطرفية). يمكننا أيضًا استلام هذه الأخطاء باستعمال دالة مُعاملة الأحداث العمومية كما وضّحنا في الفصل ….
</p>

<p>
	<strong>ملاحظة</strong>: "<code>async/await</code> و<code>promise.then/catch</code>" عندما نستخدم <code>async/await</code> نادرًا ما نحتاج إلى <code>‎.then</code> وذلك لأن <code>await</code> تعالج عملية الانتظار. ويمكننا استخدام <code>try..catch</code> بدلًا من <code>‎.catch</code>. وهذه عادةً (ليس دائمًا) ما تكون أكثر ملاءمة.
</p>

<p>
	ولكن في الشيفرات ذات هرمية أعلى (مستوى أعلى)، عندما نكون خارج أي دالّة غير متزامنة، يتعذر علينا استخدام <code>await</code> لذا من المعتاد إضافة <code>.then/catch</code> لمعالجة النتيجة النهائية أو الأخطاء المتساقطة.
</p>

<p>
	كما هو الحال في السطر <code>(*)</code> من المثال أعلاه.
</p>

<p>
	<strong>ملاحظة</strong>: "تعمل <code>async/await</code> مع <code>Promise.all</code>" عندما نحتاج لانتظار عدة وعود يمكننا أن نغلفها في تابع <code>Promise.all</code> وبعده <code>await</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_44" style="">
<span class="com">// انتظر مصفوفة النتائج </span><span class="pln">
let results </span><span class="pun">=</span><span class="pln"> await </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">([</span><span class="pln">
  fetch</span><span class="pun">(</span><span class="pln">url1</span><span class="pun">),</span><span class="pln">
  fetch</span><span class="pun">(</span><span class="pln">url2</span><span class="pun">),</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
</span><span class="pun">]);</span></pre>

<p>
	وإن حدث خطأ ما، فإنه سيُنقل من الوعد نفسه إلى التابع <code>Promise.all</code>، وبعدها يصبح استثناءً يمكننا التقاطه باستخدام <code>try..catch</code> حول الاستدعاء.
</p>

<h2>
	خلاصة
</h2>

<p>
	لكلمة <code>async</code> المفتاحية قبل الدوال تأثيرين اثنين:
</p>

<ol>
<li>
		تحوّلها لتُعيد وعدًا دومًا.
	</li>
	<li>
		تتيح استعمال <code>await</code> فيها.
	</li>
</ol>
<p>
	حين يرى محرّك جافاسكربت الكلمة المفتاحية <code>await</code> قبل الوعود، ينتظر حتّى يُنجز الوعد ومن ثمّ:
</p>

<ol>
<li>
		لو كان خطأ فسيُولِّد الاستثناء كما لو استعملنا <code>throw error</code>.
	</li>
	<li>
		وإلّا أعاد الناتج.
	</li>
</ol>
<p>
	تقدّم لنا هتين الكلمتين معًا إطار عمل رائع نكتب به شيفرات غير متزامنة تسهل علينا قراءتها كما وكتابتها.
</p>

<p>
	نادرًا ما نستعمل <code>promise.then/catch</code> بوجود <code>async/await</code>، ولكن علينا ألّا ننسى بأنّ الأخيرتين مبنيّتين على الوعود إذ نضطر أحيانًا (خارج الدوال مثلًا) استعمال <code>promise.then/catch</code>. كما وأنّ <code>Promise.all</code> جميل جدًا لننتظر أكثر من مهمّة في وقت واحد.
</p>

<h2>
	تمارين
</h2>

<h3>
	إعادة الكتابة باستعمال async/await
</h3>

<p>
	أعِد كتابة الشيفرة في المثال من الفصل سلسلة الوعود باستعمال <code>async/await</code> بدل <code>‎.then/catch</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_46" style="">
<span class="kwd">function</span><span class="pln"> loadJson</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status </span><span class="pun">==</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">
      </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

loadJson</span><span class="pun">(</span><span class="str">'no-such-user.json'</span><span class="pun">)</span><span class="pln"> </span><span class="com">// (3)</span><span class="pln">
  </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Error: 404</span></pre>

<h4>
	الحل
</h4>

<p>
	ترى الملاحظات أسفل الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_48" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> loadJson</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (1)</span><span class="pln">
  let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (2)</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status </span><span class="pun">==</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let json </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln"> </span><span class="com">// (3)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> json</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

loadJson</span><span class="pun">(</span><span class="str">'no-such-user.json'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Error: 404 (4)</span></pre>

<p>
	ملاحظات:
</p>

<ol>
<li>
		<p>
			الدالّة <code>loadJson</code> تصبح <code>async</code>.
		</p>
	</li>
	<li>
		<p>
			جميع المحتوى في <code>.then</code> يستبدل بـِ <code>await</code>.
		</p>
	</li>
	<li>
		<p>
			يمكننا إعادة <code>return response.json()‎</code> بدلًا من انتظارها. هكذا:
		</p>

		<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_50" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status </span><span class="pun">==</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln"> </span><span class="com">// (3)</span><span class="pln">
</span><span class="pun">}</span></pre>

		<p>
			ثم الشيفرة الخارجية ستنتظر <code>await</code> لينفذ الوعد في حالتنا الأمر غير مهم.
		</p>
	</li>
	<li>
		<p>
			سيرمى الخطأ من التابع <code>loadJson</code> المعالج من قبل <code>‎.catch</code>. لن نستطيع استخدام <code>await loadJson(…)‎</code> هنا، وذلك لأننا لسنا في دالّة غير متزامنة.
		</p>
	</li>
</ol>
<h3>
	أعِد كتابة <code>rethrow</code> باستعمال async/await
</h3>

<p>
	في الشيفرة أدناه مثلًا عن إعادة الرمي من فصل سلسلة الوعود. أعد كتابته باستخدام <code>async/await</code> بدلًا من <code>‎.then/catch</code>. وتخلص من العودية لصالح الحلقة في <code>demoGithubUser</code>: مع استخدام <code>async/await</code> سيسهلُ الأمر.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_52" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">HttpError</span><span class="pln"> extends </span><span class="typ">Error</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">response</span><span class="pun">.</span><span class="pln">url</span><span class="pun">}`);</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'HttpError'</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">response </span><span class="pun">=</span><span class="pln"> response</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> loadJson</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status </span><span class="pun">==</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">
      </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HttpError</span><span class="pun">(</span><span class="pln">response</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// ‫اطلب اسم المستخدم إلى أن يعيد لك github مستخدم صحيح</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> demoGithubUser</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let name </span><span class="pun">=</span><span class="pln"> prompt</span><span class="pun">(</span><span class="str">"Enter a name?"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"iliakan"</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> loadJson</span><span class="pun">(`</span><span class="pln">https</span><span class="pun">:</span><span class="com">//api.github.com/users/${name}`)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">user </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      alert</span><span class="pun">(`</span><span class="typ">Full</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}.`);</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> user</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err instanceof </span><span class="typ">HttpError</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> err</span><span class="pun">.</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status </span><span class="pun">==</span><span class="pln"> </span><span class="lit">404</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        alert</span><span class="pun">(</span><span class="str">"No such user, please reenter."</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> demoGithubUser</span><span class="pun">();</span><span class="pln">
      </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> err</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

demoGithubUser</span><span class="pun">();</span></pre>

<h4>
	الحل
</h4>

<p>
	لا يوجد أي صعوبة هنا، إذ كل ما عليك فعله هو استبدال <code>try...catch</code> بدلًا من <code>‎.catch</code> بداخل تابع <code>demoGithubUser</code> وأضف <code>async/await</code> عند الحاجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_54" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">HttpError</span><span class="pln"> extends </span><span class="typ">Error</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">response</span><span class="pun">.</span><span class="pln">url</span><span class="pun">}`);</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'HttpError'</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">response </span><span class="pun">=</span><span class="pln"> response</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

async </span><span class="kwd">function</span><span class="pln"> loadJson</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status </span><span class="pun">==</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HttpError</span><span class="pun">(</span><span class="pln">response</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// ‫اطلب اسم المستخدم إلى أن يعيد لك github مستخدم صحيح</span><span class="pln">
async </span><span class="kwd">function</span><span class="pln"> demoGithubUser</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  let user</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">while</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let name </span><span class="pun">=</span><span class="pln"> prompt</span><span class="pun">(</span><span class="str">"Enter a name?"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"iliakan"</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      user </span><span class="pun">=</span><span class="pln"> await loadJson</span><span class="pun">(`</span><span class="pln">https</span><span class="pun">:</span><span class="com">//api.github.com/users/${name}`);</span><span class="pln">
      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln"> </span><span class="com">// لا يوجد خطأ اخرج من الحلقة</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err instanceof </span><span class="typ">HttpError</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> err</span><span class="pun">.</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status </span><span class="pun">==</span><span class="pln"> </span><span class="lit">404</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// ‫تستمر الحلقة بعد alert</span><span class="pln">
        alert</span><span class="pun">(</span><span class="str">"No such user, please reenter."</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// ‫خطأ غير معروف , أعد رميه rethrow</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> err</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">      
  </span><span class="pun">}</span><span class="pln">


  alert</span><span class="pun">(`</span><span class="typ">Full</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}.`);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> user</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

demoGithubUser</span><span class="pun">();</span></pre>

<h3>
	استدعاء async من دالة غير متزامنة
</h3>

<p>
	لدينا دالة ”عادية“، ونريد استدعاء <code>async</code> منها واستعمال ناتجها، كيف؟
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_56" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> wait</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  await </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">));</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...ماذا نكتب هنا؟</span><span class="pln">
  </span><span class="com">// ‫علينا استدعاء async wait()‎ والانتظار حتّى تأتي 10</span><span class="pln">
  </span><span class="com">// ‫لا تنسَ، لا يمكن استعمال ”await“ هنا</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ملاحظة: هذه المهمّة (تقنيًا) بسيطة جدًا، ولكنّ السؤال شائع بين عموم المطوّرين الجدد على async/await.
</p>

<h4>
	الحل
</h4>

<p>
	هنا تأتي فائدة معرفة طريقة عمل هذه الأمور.
</p>

<p>
	ليس عليك إلّا معاملة استدعاء <code>async</code> وكأنّه وعد ووضع تابِع <code>‎.then</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8460_58" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> wait</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  await </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">));</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> f</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// يعرض 10 بعد ثانية واحدة</span><span class="pln">
  wait</span><span class="pun">().</span><span class="pln">then</span><span class="pun">(</span><span class="pln">result </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

f</span><span class="pun">();</span></pre>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/async-await" rel="external nofollow">Async/await</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">921</guid><pubDate>Fri, 19 Jun 2020 11:07:03 +0000</pubDate></item><item><title>&#x627;&#x644;&#x645;&#x647;&#x627;&#x645; &#x627;&#x644;&#x633;&#x631;&#x64A;&#x639;&#x629; Microtasks &#x645;&#x642;&#x627;&#x628;&#x644; &#x627;&#x644;&#x648;&#x639;&#x62F; &#x641;&#x64A; &#x62A;&#x646;&#x641;&#x64A;&#x630; &#x627;&#x644;&#x645;&#x647;&#x627;&#x645; &#x644;&#x627;&#x62D;&#x642;&#x64B;&#x627; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D9%87%D8%A7%D9%85-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9%D8%A9-microtasks-%D9%85%D9%82%D8%A7%D8%A8%D9%84-%D8%A7%D9%84%D9%88%D8%B9%D8%AF-%D9%81%D9%8A-%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A7%D9%84%D9%85%D9%87%D8%A7%D9%85-%D9%84%D8%A7%D8%AD%D9%82%D9%8B%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r920/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/76.jpg.fd483c87c170a1c10a2c73b8054c578e.jpg" /></p>

<p>
	دوال المُعاملة للوعود <code>‎.then</code> و <code>‎.catch</code> و<code>‎.finally</code> هي دوال غير متزامنة، دومًا.
</p>

<p>
	فحتّى لو سويَ الوعد مباشرةً (أي سواءً أنُجز أو رُفض) فالشيفرة أسفل <code>‎.then</code> و<code>‎.catch</code> و<code>‎.finally</code> ستُنفّذ حتّى قبل دوال المعاملة.
</p>

<p>
	لاحِظ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2043_7" style="">
<span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">();</span><span class="pln">

promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">"promise done!"</span><span class="pun">))</span><span class="pln"> </span><span class="com">// اكتمل الوعد</span><span class="pln">

alert</span><span class="pun">(</span><span class="str">"code finished"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫نرى هذا النص أولًا (انتهت الشيفرة)</span></pre>

<p>
	لو شغّلته فسترى أولًا <code>code finished</code> وبعدها ترى <code>promise done!</code>.
</p>

<p>
	هذا… غريب إذ أن الوعد أنجز قطعًا في البداية.
</p>

<p>
	لماذا شُغّلت <code>‎.then</code> بعدئذ؟ ما الّذي يحدث؟
</p>

<h2>
	طابور المهام السريعة
</h2>

<p>
	تطلب الدوال غير المتزامنة عملية إدارة مضبوطة. ولهذا تحدّد مواصفة اللغة طابورًا داخليًا باسم <code>PromiseJobs</code> (غالبًا ما نسمّيه ”بطابور المهام السريعة“ Microtask Queue حسب مصطلح محرّك V8).
</p>

<p>
	تقول <a href="https://tc39.github.io/ecma262/#sec-jobs-and-job-queues" rel="external nofollow">المواصفة</a>:
</p>

<ul>
<li>
		الطابور بمبدأ ”أوّل من يدخل هو أوّل من يخرج“: المهام التي تُضاف أولًا تُنفّذ أولًا.
	</li>
	<li>
		لا يبدأ تنفيذ المهمة إلّا لو لم يكن هناك شيء آخر يعمل.
	</li>
</ul>
<p>
	وبعبارة أبسط، فمتى جهز الوعد تُضاف دوال المعاملة <code>‎.then/catch/finally</code> إلى الطابور، وتبقى هناك بلا تنفيذ. متى وجد محرّك جافاسكربت نفسه قد فرغ من الشيفرة الحالية، يأخذ مهمة من الطابور وينفّذها.
</p>

<p>
	لهذا السبب نرى ”اكتملت الشيفرة“ في المثال أعلاه أولًا.
</p>

<p style="text-align: center;">
	<img class="ipsImage ipsImage_thumbnailed" data-fileid="82487" data-unique="o52hqde5z" src="https://academy.hsoub.com/uploads/monthly_2021_11/promiseQueue.png.3f7e2d9a3f46796b01dc74cfe62691f3.png" alt="promiseQueue.png"></p>

<p>
	دوال معاملة الوعود تمرّ من الطابور الداخلي هذا دومًا.
</p>

<p>
	لو كانت في الشيفرة سلسلة من <code>‎.then/catch/finally</code> فستُنفّذ كلّ واحدة منها تنفيذًا غير متزامن. أي أنّ الأولى تُضاف إلى الطابور وتُنفّذ متى اكتمل تنفيذ الشيفرة الحالية وانتهت دوال المُعاملة الّتي أُضيفت إلى الطابور مسبقًا.
</p>

<p>
	<strong>ولكن ماذا لو كان الترتيب يهمّنا؟ كيف نشغّل <code>code finished</code> بعد <code>promise done</code>؟</strong>
</p>

<p>
	بسيطة، نضعها في الطابور باستعمال <code>‎.then</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2043_9" style="">
<span class="typ">Promise</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">()</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">"promise done!"</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">"code finished"</span><span class="pun">));</span></pre>

<p>
	هكذا صار الترتيب كما نريد.
</p>

<h2>
	الرفض غير المعالج
</h2>

<p>
	تذكر حدث <code>unhandledrejection</code> من فصل التعامل مع الأخطاء في الوعود.
</p>

<p>
	سنرى الآن كيف تعرف محرّكات جافاسكربت ما إن وُجدت حالة رفض لم يُتعامل معها، أم لا.
</p>

<p>
	<strong>تحدث ”حالة الرفض لم يُتعامل معها“ حين لا يتعامل شيء مع خطأ أنتجه وعد في آخر طابور المهام السريعة.</strong>
</p>

<p>
	عادةً لو كنّا نتوقّع حدوث خطأ نُضيف التابِع <code>‎.catch</code> إلى سلسلة الوعود للتعامل معه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2043_11" style="">
<span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Promise Failed!"</span><span class="pun">));</span><span class="pln">
promise</span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">'caught'</span><span class="pun">));</span><span class="pln"> </span><span class="com">// هكذا</span><span class="pln">

</span><span class="com">// لا يعمل هذا السطر إذ تعاملنا مع الخطأ</span><span class="pln">
window</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'unhandledrejection'</span><span class="pun">,</span><span class="pln"> event </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">reason</span><span class="pun">));</span></pre>

<p>
	ولكن… لو نسينا وضع <code>‎.catch</code> سيُشغّل المحرّك هذا الحدث متى فرغ طابور المهام السريعة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2043_13" style="">
<span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Promise Failed!"</span><span class="pun">));</span><span class="pln">

</span><span class="com">// Promise Failed!</span><span class="pln">
window</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'unhandledrejection'</span><span class="pun">,</span><span class="pln"> event </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">reason</span><span class="pun">));</span></pre>

<p>
	وماذا لو تعاملنا مع الخطأ لاحقًا؟ هكذا مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2043_15" style="">
<span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Promise Failed!"</span><span class="pun">));</span><span class="pln">
setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> promise</span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">'caught'</span><span class="pun">)),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln"> </span><span class="com">// لاحِظ</span><span class="pln">

</span><span class="com">// Error: Promise Failed!</span><span class="pln">
window</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'unhandledrejection'</span><span class="pun">,</span><span class="pln"> event </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">reason</span><span class="pun">));</span></pre>

<p>
	إذا شغلناه الآن سنرى <code>Promise Failed!</code> أولًا ثم <code>caught</code>.
</p>

<p>
	لو بقينا نجهل طريقة عمل طابور المهام السريعة فسنتساءل: ”لماذا عملت دالة المُعاملة <code>unhandledrejection</code>؟ الخطأ والتقطناه!“.
</p>

<p>
	أمّا الآن فنعرف أنّه لا يُولّد <code>unhandledrejection</code> إلّا حين انتهاء طابور المهام السريعة: فيفحص المحرّك الوعود وإن وجد حالة ”رفض“ في واحدة، يشغّل الحدث.
</p>

<p>
	في المثال أعلاه، أضيفت <code>.catch</code> وشغّلت من قِبل <code>setTimeout</code> متأخرةً بعد حدوث <code>unhandledrejection</code> لذا فإن ذلك لم يغيّر أي شيء.
</p>

<h2>
	خلاصة
</h2>

<p>
	التعامل مع الوعود دومًا يكون غير متزامن، إذ تمرّ إجراءات الوعود في طابور داخلي ”لمهام الوعود“ أو ما نسمّيه ”بطابور المهام السريعة“ (مصطلح المحرّك V8).
</p>

<p>
	بهذا لا تُستدعى دوال المُعاملة <code>‎.then/catch/finally</code> إلّا بعد اكتمال الشيفرة الحالية.
</p>

<p>
	ولو أردنا أن نضمن تشغيل هذه الأسطر بعينها بعد <code>‎.then/catch/finally</code> فيمكننا إضافتها إلى استدعاء <code>‎.then</code> في السلسلة.
</p>

<p>
	في معظم محركات جافاسكربت، بما في ذلك المتصفحات و Node.js، يرتبط مفهوم المهام السريعة ارتباطًا وثيقًا بـ "حلقة الأحداث" والمهام الكبيرة "macrotasks". نظرًا لأنها لا تملك علاقة مباشرة بالوعود، لذا فإننا شرحناها في جزء آخر من السلسلة التعليمية، في الفصل <a href="https://javascript.info/event-loop" rel="external nofollow">Event loop: microtasks and macrotasks</a>.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/microtask-queue" rel="external nofollow">Microtasks</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">920</guid><pubDate>Thu, 30 Jul 2020 18:04:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62F;&#x648;&#x627;&#x644; &#x627;&#x644;&#x648;&#x627;&#x639;&#x62F;&#x629;: &#x62A;&#x62D;&#x648;&#x64A;&#x644; &#x627;&#x644;&#x62F;&#x648;&#x627;&#x644; &#x625;&#x644;&#x649; &#x648;&#x639;&#x648;&#x62F; Promisification &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D9%88%D8%A7%D8%B9%D8%AF%D8%A9-%D8%AA%D8%AD%D9%88%D9%8A%D9%84-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A5%D9%84%D9%89-%D9%88%D8%B9%D9%88%D8%AF-promisification-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r919/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/75.jpg.0f5448e7ad12a5adc23848a8127a6c5d.jpg" /></p>

<p>
	تحويل الدوال إلى وعود (Promisification) هي عملية تغليف الدالة التي تستلم ردّ نداء لتصبح دالة تُعيد وعدًا.
</p>

<p>
	وفي الحياة العملية فهذا النوع من التحويل مطلوب جدًا إذ تعتمد العديد من الدوال والمكتبات على ردود النداء. ولكن… الوعود أسهل وأفضل لذا من المنطقي تحويل تلك الدوال.
</p>

<p>
	لنأخذ مثلًا دالة <code>loadScript(src, callback)‎</code> من الفصل <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%B1%D8%AF%D9%88%D8%AF-%D8%A7%D9%84%D9%86%D8%AF%D8%A7%D8%A1-callbacks-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r914/" rel="">مقدمة إلى ردود النداء callback</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3401_7" style="">
<span class="kwd">function</span><span class="pln"> loadScript</span><span class="pun">(</span><span class="pln">src</span><span class="pun">,</span><span class="pln"> callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let script </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'script'</span><span class="pun">);</span><span class="pln">
  script</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> src</span><span class="pun">;</span><span class="pln">

  script</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> callback</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> script</span><span class="pun">);</span><span class="pln">
  script</span><span class="pun">.</span><span class="pln">onerror </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> callback</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(`</span><span class="typ">Script</span><span class="pln"> load error </span><span class="kwd">for</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">src</span><span class="pun">}`));</span><span class="pln">

  document</span><span class="pun">.</span><span class="pln">head</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// الاستعمال</span><span class="pln">
</span><span class="com">// loadScript('path/script.js', (err, script) =&gt; {...})</span></pre>

<p>
	هيا نحوّلها. على الدالة الجديدة <code>loadScriptPromise(src)‎</code> القيام بنفس ما تقوم به تلك، ولكن لا تقبل إلّا <code>src</code> وسيطًا (بدون <code>callback</code>) وتُعيد وعدًا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3401_9" style="">
<span class="pln">let loadScriptPromise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">src</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    loadScript</span><span class="pun">(</span><span class="pln">src</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> reject</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
      </span><span class="kwd">else</span><span class="pln"> resolve</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// الاستعمال</span><span class="pln">
</span><span class="com">// loadScriptPromise('path/script.js').then(...)</span></pre>

<p>
	الآن يمكننا استعمال <code>loadScriptPromise</code> بسهولة بالغة في الشيفرات التي تعتمد الوعود.
</p>

<p>
	وكما نرى فالدالة تكلّف الدالة الأصلية <code>loadScript</code> بكلّ العمل اللازم، وما تفعله هو تقديم ردّ نداء من عندها تحوّله إلى وعد <code>resolve/reject</code>.
</p>

<p>
	نحتاج عمليًا إلى تحويل دوال عديدة وكثيرة لتعتمد الوعود، لذا من المنطقي استعمال دالة مساعِدة.
</p>

<p>
	لنسمّها <code>promisify(f)‎</code> وستقبل دالة الأصل <code>f</code> اللازم تحويلها، وتُعيد دالة غالِفة.
</p>

<p>
	يؤدّي الغلاف نفس عمل الشيفرة أعلاه: يُعيد وعدًا ويمرّر النداء إلى الدالة الأصلية <code>f</code> متتّبعًا الناتج في ردّ نداء يصنعه بنفسه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3401_11" style="">
<span class="kwd">function</span><span class="pln"> promisify</span><span class="pun">(</span><span class="pln">f</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(...</span><span class="pln">args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// يعيد التابع المغلّف</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">function</span><span class="pln"> callback</span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// ‫رد النداء خاصتنا لـِ f</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> reject</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          resolve</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

      args</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">callback</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫نضيف رد النداء خاصتنا إلى نهاية واسطاء f</span><span class="pln">

      f</span><span class="pun">.</span><span class="pln">call</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">args</span><span class="pun">);</span><span class="pln"> </span><span class="com">// استدعي التابع الأصلي</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="com">// ‫طريقة الاستخدام:</span><span class="pln">
let loadScriptPromise </span><span class="pun">=</span><span class="pln"> promisify</span><span class="pun">(</span><span class="pln">loadScript</span><span class="pun">);</span><span class="pln">
loadScriptPromise</span><span class="pun">(...).</span><span class="pln">then</span><span class="pun">(...);</span></pre>

<p>
	نفترض هنا بأنّ الدالة الأصلية تتوقّع استلام ردّ نداء له وسيطين <code>(err, result)</code>، وهذا ما نواجهه أغلب الوقت لهذا كتبنا ردّ النداء المخصّص بهذا التنسيق، وتعمل دالة <code>promisify</code> على أكمل وجه… لهذه الحالات.
</p>

<p>
	ولكن ماذا لو كانت تتوقّع <code>f</code> ردّ نداء له وسطاء أكثر <code>callback(err, res1, res2, ...)‎</code>؟
</p>

<p>
	إليك نسخة <code>promisify</code> ذكية محسّنة: لو استدعيناها هكذا <code>promisify(f, true)‎</code> فسيكون ناتج الوعد مصفوفة من نواتج ردود النداء <code>[res1, res2,‎ ...‎]</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3401_13" style="">
<span class="com">// ‫التابع promisify(f, true)‎ لجب مصفوفة النتائج</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> promisify</span><span class="pun">(</span><span class="pln">f</span><span class="pun">,</span><span class="pln"> manyArgs </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(...</span><span class="pln">args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">function</span><span class="pln"> callback</span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">results</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// ‫رد النداء خاصتنا لـِ f</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> reject</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="com">// إجلب جميع ردود النداء وإذا حدّد أكثر من وسيط </span><span class="pln">
          resolve</span><span class="pun">(</span><span class="pln">manyArgs </span><span class="pun">?</span><span class="pln"> results </span><span class="pun">:</span><span class="pln"> results</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

      args</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">callback</span><span class="pun">);</span><span class="pln">

      f</span><span class="pun">.</span><span class="pln">call</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">args</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="com">// ‫طريقة الاستخدام:</span><span class="pln">
f </span><span class="pun">=</span><span class="pln"> promisify</span><span class="pun">(</span><span class="pln">f</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
f</span><span class="pun">(...).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">arrayOfResults </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">...,</span><span class="pln"> err </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">...)</span></pre>

<p>
	أمّا لتنسيقات ردود النداء الشاذّة (مثل التي بدون وسيط <code>err</code> أصلًا <code>callback(result)‎</code>) فيمكننا تحويلها يدويًا بدون استعمال الدالة المساعِدة.
</p>

<p>
	كما وهناك وحدات لها دوال تحويل مرنة أكثر في التعامل مثل <a href="https://github.com/digitaldesignlabs/es6-promisify" rel="external nofollow">es6-promisify</a>. وفي Node.js نرى الدالة المضمّنة <code>util.promisify</code> لهذا الغرض.
</p>

<p>
	<strong>ملاحظة</strong>: يعدّ تحويل الدوال إلى وعود نهجًا رائعًا، خاصةً عند استخدام <code>async/await</code> (الّتي سنراها في الفصل التالي)، ولكن ليس بديلًا كليًا لردود النداء.
</p>

<p>
	تذكر أن الوعد له نتيجة واحدة فقط، ولكن تقنيًا ممكن أن تُستدعى ردود النداء عدة مرات.
</p>

<p>
	لذا فإن تحويل الدوال إلى وعود مخصصة للدوال التي تستدعي ردود النداء لمرة واحدة. وستُتجاهل جميع الاستدعاءات اللاحقة.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/promisify" rel="external nofollow">Promisification</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">919</guid><pubDate>Fri, 19 Jun 2020 09:24:14 +0000</pubDate></item><item><title>&#x648;&#x627;&#x62C;&#x647;&#x629; &#x627;&#x644;&#x648;&#x639;&#x648;&#x62F; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; Promise API &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-promise-api-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r918/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/74.jpg.5d2f69cac43ada92e7d1adb71725576a.jpg" /></p>

<p>
	ثمّة 5 توابِع ثابتة (static) في صنف الوعود <code>Promise</code>. سنشرح الآن عن استعمالاتها سريعًا.
</p>

<h2>
	Promise.all
</h2>

<p>
	لنقل بأنّك تريد تنفيذ أكثر من وعد واحد في وقت واحد، والانتظار حتّى تجهز جميعها.
</p>

<p>
	مثلًا أن تُنزّل أكثر من عنوان URL في آن واحد وتُعالج المحتوى ما إن تُنزّل كلها.
</p>

<p>
	وهذا الغرض من وجود <code>Promise.all</code>. إليك صياغته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_7" style="">
<span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">([...</span><span class="pln">promises</span><span class="pun">...]);</span></pre>

<p>
	يأخذ التابِع <code>Promise.all</code> مصفوفة من الوعود (تقنيًا يمكن أن تكون أيّ … ولكنّها في العادة مصفوفة) ويُعيد وعدًا جديدًا.
</p>

<p>
	لا يُحلّ الوعد الجديد إلّا حين تستقرّ الوعود في المصفوفة، وتصير تلك المصفوفة التي تحمل نواتج الوعود - تصير ناتج الوعد الجديد.
</p>

<p>
	مثال على ذلك تابِع <code>Promise.all</code> أسفله إذ يستقرّ بعد 3 ثوان ويستلم نواتجه في مصفوفة <code>[1, 2, 3]</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_9" style="">
<span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">([</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="lit">1</span><span class="pun">),</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">)),</span><span class="pln"> </span><span class="com">// 1</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="lit">2</span><span class="pun">),</span><span class="pln"> </span><span class="lit">2000</span><span class="pun">)),</span><span class="pln"> </span><span class="com">// 2</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="lit">3</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">))</span><span class="pln">  </span><span class="com">// 3</span><span class="pln">
</span><span class="pun">]).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1,2,3 ما إن تجهز الوعود يُعطي كلّ واحد عنصرًا في المصفوفة</span></pre>

<p>
	لاحِظ كيف أنّ ترتيب عناصر المصفوفة الناتجة يتطابق مع ترتيب الوعود، فحتّى لو أخذ الوعد الأوّل وقتًا أطول من غيره حتّى يُحلّ، فسيظلّ العنصر الأول في مصفوفة النواتج.
</p>

<p>
	نستعمل عادةً حيلة صغيرة أن نصنع مصفوفة جديدة بخارطة تأخذ القديمة وتحوّلها إلى وعود، ثمّ نمرّر ذلك كلّه إلى <code>Promise.all</code>.
</p>

<p>
	فمثلًا لو لدينا مصفوفة من العناوين، يمكننا جلبها كلّها هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_11" style="">
<span class="pln">let urls </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="str">'https://api.github.com/users/iliakan'</span><span class="pun">,</span><span class="pln">
  </span><span class="str">'https://api.github.com/users/remy'</span><span class="pun">,</span><span class="pln">
  </span><span class="str">'https://api.github.com/users/jeresig'</span><span class="pln">
</span><span class="pun">];</span><span class="pln">

</span><span class="com">// ‫نحوّل كلّ عنوان إلى وعد التابِع fetch</span><span class="pln">
let requests </span><span class="pun">=</span><span class="pln"> urls</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">url </span><span class="pun">=&gt;</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">));</span><span class="pln">

</span><span class="com">// ‫ينتظر Promise.all حتّى تُحلّ كلّ المهام</span><span class="pln">
</span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">(</span><span class="pln">requests</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">responses </span><span class="pun">=&gt;</span><span class="pln"> responses</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">
    response </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">response</span><span class="pun">.</span><span class="pln">url</span><span class="pun">}:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status</span><span class="pun">}`)</span><span class="pln">
  </span><span class="pun">));</span></pre>

<p>
	من أكبر الأمثلة على جلب المعلومات. هي جلب المعلومات لمجموعة من مستخدمي موقع GitHub من خلال اسمائهم (يمكننا جلب مجموعة من السلع بحسب رقم المعرف الخاص بها، منطق الحل متشابه في كِلا الحالتين):
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_13" style="">
<span class="pln">let names </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'iliakan'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'remy'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'jeresig'</span><span class="pun">];</span><span class="pln">

let requests </span><span class="pun">=</span><span class="pln"> names</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">name </span><span class="pun">=&gt;</span><span class="pln"> fetch</span><span class="pun">(`</span><span class="pln">https</span><span class="pun">:</span><span class="com">//api.github.com/users/${name}`));</span><span class="pln">

</span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">(</span><span class="pln">requests</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">responses </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// استدعيت جميع الردود بنجاح</span><span class="pln">
    </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let response of responses</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      alert</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">response</span><span class="pun">.</span><span class="pln">url</span><span class="pun">}:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status</span><span class="pun">}`);</span><span class="pln"> </span><span class="com">// ‫أظهر 200 من أجل كلّ عنوان url</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> responses</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
  </span><span class="com">// ‫اربط مصفوفة الردود مع مصفوفة response.json()‎ لقراءة المحتوى</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">responses </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">(</span><span class="pln">responses</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">r </span><span class="pun">=&gt;</span><span class="pln"> r</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())))</span><span class="pln">
  </span><span class="com">// ‫تحلل جميع الإجابات : وتكوّن مصفوفة "users" منهم</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">users </span><span class="pun">=&gt;</span><span class="pln"> users</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">user </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">)));</span></pre>

<p>
	<strong>لو رُفض أيّ وعد من الوعود، سيرفض الوعد الذي يُعيده <code>Promise.all</code> مباشرةً بذلك الخطأ.</strong>
</p>

<p>
	مثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_15" style="">
<span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">([</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="lit">1</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">)),</span><span class="pln">
  </span><span class="com">// هنا</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">)),</span><span class="pln"> </span><span class="lit">2000</span><span class="pun">)),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="lit">3</span><span class="pun">),</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">))</span><span class="pln">
</span><span class="pun">]).</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Error: Whoops!</span></pre>

<p>
	نرى هنا أنّ الوعد الثاني يُرفض بعد ثانيتين، ويؤدّي ذلك إلى رفض <code>Promise.all</code> كاملًا، وهكذا يُنفّذ التابِع <code>‎.catch</code> ويصير الخطأ ناتج <code>Promise.all</code> كلّه.
</p>

<p>
	<strong>تحذير</strong>: في حالة حدوث خطأ ما، تُتجاهل الوعود الأُخرى لو أن وعدًا ما رُفضَ، ستُرفض جميع الوعود من خلال تابع <code>Promise.all</code> مباشرةً، متناسيًا بذلك الوعود الأخرى في القائمة. وعندها ستُتجاهل نتائجها أيضًا.
</p>

<p>
	على سبيل المثال، إن كان العديد من استدعاءات <code>fetch</code> كما هو موضح في المثال أعلاه، وفشِل أحدها، فسيستمر تنفيذ الاستدعاءات الأخرى. ولكن لن يشاهدها تابع <code>Promise.all</code> بعد الآن. ربما ستُنجز بقية الوعود، ولكن نتائجها ستُتجاهل في نهاية المطاف.
</p>

<p>
	لن يُلغي التابع <code>Promise.all</code> الوعود الأخرى، إذ لا يوجد مثل هذا الفعل في الوعود، سنغطي في فصل آخر طريقة المتبعة في إلغاء الوعود وسيساعدنا التابع <code>AbortController</code> في ذلك، ولكنه ليس جزءًا من واجهة الوعود البرمجية.
</p>

<p>
	<strong>ملاحظة</strong>: يسمح التابع <code>Promise.all(iterable)‎</code> لغير الوعود (القيم النظامية) في <code>iterable</code>. يقبل التابع <code>Promise.all(...)‎</code> وعودًا قابلة للتكرار (مصفوفات في معظم الوقت). ولكن لو لم تكُ تلك الكائنات وعودًا. فستُمرّر النتائج كما هي.
</p>

<p>
	فمثلًا، النتائج هنا <code>[1, 2, 3]</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_17" style="">
<span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">([</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="lit">1</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}),</span><span class="pln">
  </span><span class="lit">2</span><span class="pun">,</span><span class="pln">
  </span><span class="lit">3</span><span class="pln">  
</span><span class="pun">]).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1, 2, 3</span></pre>

<p>
	يمكننا تمرير القيم الجاهزة لتابِع <code>Promise.all</code> عندما يكون ذلك مناسبًا.
</p>

<h2>
	Promise.allSettled
</h2>

<p>
	<strong>إضافة حديثة</strong> هذه إضافة حديثة للغة، وقد يحتاج إلى تعويض نقص الدعم في المتصفحات القديمة.
</p>

<p>
	لو رُفض أحد الوعود في <code>Promise.all</code> فسيُرفض كلّه مجتمعًا. هذا يفيدنا لو أردنا استراتيجية ”إمّا كل شيء أو لا شيء“، أي حين نطلب وجود النتائج كلها كي نواصل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_19" style="">
<span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">([</span><span class="pln">
  fetch</span><span class="pun">(</span><span class="str">'/template.html'</span><span class="pun">),</span><span class="pln">
  fetch</span><span class="pun">(</span><span class="str">'/style.css'</span><span class="pun">),</span><span class="pln">
  fetch</span><span class="pun">(</span><span class="str">'/data.json'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">]).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">render</span><span class="pun">);</span><span class="pln"> </span><span class="com">// تابِع التصيير يطلب نواتج كلّ توابِع الجلب</span></pre>

<p>
	بينما ينتظر <code>Promise.allSettled</code> استقرار كلّ الوعود. للمصفوفة الناتجة هذه العناصر:
</p>

<ul>
<li>
		<code>{status:"fulfilled", value:result}</code> من أجل الاستجابات الناجحة.
	</li>
	<li>
		<code>‎{status:"rejected", reason:error}‎</code> من أجل الأخطاء.
	</li>
</ul>
<p>
	مثال على ذلك هو لو أردنا جلب معلومات أكثر من مستخدم واحد. فلو فشل أحد الطلبات ما زلنا نريد معرفة معلومات البقية.
</p>

<p>
	فلنستعمل <code>Promise.allSettled</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_21" style="">
<span class="pln">let urls </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="str">'https://api.github.com/users/iliakan'</span><span class="pun">,</span><span class="pln">
  </span><span class="str">'https://api.github.com/users/remy'</span><span class="pun">,</span><span class="pln">
  </span><span class="str">'https://no-such-url'</span><span class="pln">
</span><span class="pun">];</span><span class="pln">

</span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">allSettled</span><span class="pun">(</span><span class="pln">urls</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">url </span><span class="pun">=&gt;</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">results </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">
    results</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">result</span><span class="pun">,</span><span class="pln"> num</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">status </span><span class="pun">==</span><span class="pln"> </span><span class="str">"fulfilled"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        alert</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">urls</span><span class="pun">[</span><span class="pln">num</span><span class="pun">]}:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">result</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">status</span><span class="pun">}`);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">status </span><span class="pun">==</span><span class="pln"> </span><span class="str">"rejected"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        alert</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">urls</span><span class="pun">[</span><span class="pln">num</span><span class="pun">]}:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">result</span><span class="pun">.</span><span class="pln">reason</span><span class="pun">}`);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">});</span></pre>

<p>
	ستكون قيمة <code>results</code> في السطر <code>(*)</code> أعلاه كالآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_23" style="">
<span class="pun">[</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">status</span><span class="pun">:</span><span class="pln"> </span><span class="str">'fulfilled'</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">:</span><span class="pln"> </span><span class="pun">...ردّ...},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">status</span><span class="pun">:</span><span class="pln"> </span><span class="str">'fulfilled'</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">:</span><span class="pln"> </span><span class="pun">...ردّ...},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">status</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rejected'</span><span class="pun">,</span><span class="pln"> reason</span><span class="pun">:</span><span class="pln"> </span><span class="pun">...كائن</span><span class="pln"> </span><span class="pun">خطأ...}</span><span class="pln">
</span><span class="pun">]</span></pre>

<p>
	هكذا نستلم لكلّ وعد حالته وقيمته أو الخطأ <code>value/error</code>.
</p>

<h3>
	تعويض نقص الدعم
</h3>

<p>
	لو لم يكن المتصفّح يدعم <code>Promise.allSettled</code> فمن السهل تعويض ذلك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_25" style="">
<span class="kwd">if</span><span class="pun">(!</span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">allSettled</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">allSettled </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">promises</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">(</span><span class="pln">promises</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">p</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">value </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">({</span><span class="pln">
      state</span><span class="pun">:</span><span class="pln"> </span><span class="str">'fulfilled'</span><span class="pun">,</span><span class="pln">
      value
    </span><span class="pun">}),</span><span class="pln"> reason </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">({</span><span class="pln">
      state</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rejected'</span><span class="pun">,</span><span class="pln">
      reason
    </span><span class="pun">}))));</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في هذه الشيفرة يأخذ التابِع <code>promises.map</code> قيم الإدخال ويحوّلها إلى وعود (في حال وصله شيء ليس بالوعد) ذلك باستعمال <code>p =&gt; Promise.resolve(p)‎</code>، بعدها يُضيف دالة المُعاملة <code>‎.then</code> لكلّ وعد.
</p>

<p>
	هذه الدالة تحوّل النواتج الناجحة <code>v</code> إلى <code>{state:'fulfilled', value:v}</code>، والأخطاء <code>r</code> إلى <code>{state:'rejected', reason:r}</code>. هذا التنسيق الذي يستعمله <code>Promise.allSettled</code> بالضبط.
</p>

<p>
	يمكننا لآن استعمال <code>Promise.allSettled</code> لجلب نتائج كلّ الوعود الممرّرة حتّى لو رفضت بعضها.
</p>

<h2>
	Promise.race
</h2>

<p>
	يشبه التابِع <code>Promise.all</code> إلّا أنّه ينتظر استقرار وعد واحد فقط ويأخذ ناتجه (أو الخطأ). صياغته هي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_27" style="">
<span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">race</span><span class="pun">(</span><span class="pln">iterable</span><span class="pun">);</span></pre>

<p>
	فمثلًا سيكون الناتج هنا <code>1</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_29" style="">
<span class="typ">Promise</span><span class="pun">.</span><span class="pln">race</span><span class="pun">([</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="lit">1</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">)),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">)),</span><span class="pln"> </span><span class="lit">2000</span><span class="pun">)),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="lit">3</span><span class="pun">),</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">))</span><span class="pln">
</span><span class="pun">]).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span></pre>

<p>
	أوّل الوعود هنا كان أسرعها وهكذا صار هو الناتج. ومتى ”فاز أوّل وعد مستقرّ بالسباق“، تُهمل بقية النواتج/الأخطاء.
</p>

<h2>
	Promise.resolve/reject
</h2>

<p>
	نادرًا ما نستعمل <code>Promise.resolve</code> و <code>Promise.reject</code> في الشيفرات الحديثة إذ صياغة <code>async/await</code> (نتكلم عنها في مقال لاحق)
</p>

<p>
	نشرحها هنا ليكون شرحًا كاملًا، وأيضًا لمن لا يستطيع استعمال <code>async/await</code> لسبب أو لآخر.
</p>

<ul>
<li>
		<code>Promise.resolve(value)‎</code> يُنشئ وعد مُنجز بالناتج<code>value</code>.
	</li>
</ul>
<p>
	ويشبه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_31" style="">
<span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="pln">value</span><span class="pun">));</span></pre>

<p>
	يستخدم هذا التابع للتوافقية، عندما يُتوقع من تابع ما أن يُعيد وعدًا.
</p>

<p>
	فمثلًا، يجلب التابِع أدناه <code>loadCached</code> عنوان <code>URL</code> ويخزن المحتوى في الذاكرة المؤقتة. ومن أجل الاستدعاءات اللاحقة لنفس عنوان <code>URL</code> سيحصل على المحتوى فورًا من الذاكرة المؤقتة، ولكنه يستخدم التابع <code>Promise.resolve</code> لتقديم وعدًا بهذا الأمر. لتكون القيمة المرتجعة دائمًا وعدًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_33" style="">
<span class="pln">let cache </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Map</span><span class="pun">();</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> loadCached</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">cache</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="pln">url</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">cache</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">url</span><span class="pun">));</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">

  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">text</span><span class="pun">())</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">text </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      cache</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">url</span><span class="pun">,</span><span class="pln">text</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> text</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكننا كتابة <code>loadCached(url).then(…)‎</code> لأننا نضمن أن التابِع سيُعيد وعدًا. كما يمكننا دائمًا استخدام <code>‎.then</code> بعد تابِع <code>loadCached</code>. وهذا هو الغرض من <code>Promise.resolve</code> في السطر <code>(*)</code>.
</p>

<h3>
	Promise.reject
</h3>

<ul>
<li>
		<code>Promise.reject(error)‎</code> ينشئ وعد مرفوض مع خطأ. ويشبه:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4943_35" style="">
<span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> reject</span><span class="pun">(</span><span class="pln">error</span><span class="pun">));</span></pre>

<p>
	عمليًا لا نستعمل هذا التابِع أبدًا.
</p>

<h2>
	خلاصة
</h2>

<p>
	لصنف الوعود <code>Promise</code> خمس توابِع ثابتة:
</p>

<ol>
<li>
		<code>Promise.all(promises)‎</code> -- ينتظر جميع الوعود لتعمل ويعيد مصفوفة بنتائجهم. وإذا رُفض أي وعدٍ منهم، سيرجع <code>Promise.all</code> خطأً، وسيتجاهلُ جميع النتائج الأخرى.
	</li>
	<li>
		<code>Promise.allSettled(promises)‎</code> -- (التابع مضاف حديثًا) ينتظر جميع الوعود لتُنجز ليُعيد نتائجها كمصفوفة من الكائنات تحتوي على: -<code>state</code>: الحالة وتكون إما <code>"fulfilled"</code> أو <code>"rejected"</code>. -<code>value</code>: القيمة (إذا تحقق الوعد) أو <code>reason</code> السبب (إذا رُفض).
	</li>
	<li>
		<code>Promise.race(promises)‎</code> -- ينتظر الوعد الأول ليُنجز وتكون نتيجته أو الخطأ الذي سيرميه خرج هذا التابِع.
	</li>
	<li>
		<code>Promise.resolve(value)‎</code> -- ينشئ وعدًا منجزًا مع القيمة الممرّرة.
	</li>
	<li>
		<code>Promise.reject(error)‎</code> -- ينشئ وعدًا مرفوضًا مع الخطأ المُمرّر.
	</li>
</ol>
<p>
	ومن بينها <code>Promise.all</code> هي الأكثر استعمالًا عمليًا.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/promise-api" rel="external nofollow">Promise <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">918</guid><pubDate>Fri, 19 Jun 2020 09:09:11 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x623;&#x62E;&#x637;&#x627;&#x621; &#x627;&#x644;&#x648;&#x639;&#x648;&#x62F; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r917/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/73.jpg.f6e93b0f61b59c3d294080ff49cdee5a.jpg" /></p>

<p>
	تُعدّ سلاسل الوعود ممتازة في التعامل مع الأخطاء، فمتى رُفض الوعد ينتقل سير التحكّم إلى أقرب دالة تتعامل مع حالة الرفض، وهذا عمليًا يسهّل الأمور كثيرًا.
</p>

<p>
	فمثلًا نرى في الشيفرة أسفله أنّ العنوان المرّر إلى <code>fetch</code> خطأ (ما من موقع بهذا العنوان) ويتعامل التابِع <code>‎.catch</code> مع الخطأ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1439_7" style="">
<span class="pln">fetch</span><span class="pun">(</span><span class="str">'https://no-such-server.blabla'</span><span class="pun">)</span><span class="pln"> </span><span class="com">// هذا الموقع من وحي الخيال العلمي</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
  </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">err</span><span class="pun">))</span><span class="pln"> </span><span class="com">// TypeError: failed to fetch (يمكن أن يتغيّر النصّ بتغيّر الخطأ)</span></pre>

<p>
	وكما ترى فليس ضروريًا أن يكون التابِع <code>‎.catch</code> مباشرةً في البداية، بل يمكن أن يظهر بعد تابِع <code>‎.then</code> واحد أو أكثر حتّى.
</p>

<p>
	أو قد يكون الموقع سليمًا ولكنّ الردّ ليس كائن JSON صالح. الطريقة الأسهل في هذه الحالة لالتقاط كلّ الأخطاء هي بإضافة تابِع <code>‎.catch</code> إلى نهاية السلسلة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1439_9" style="">
<span class="pln">fetch</span><span class="pun">(</span><span class="str">'/article/promise-chaining/user.json'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">user </span><span class="pun">=&gt;</span><span class="pln"> fetch</span><span class="pun">(`</span><span class="pln">https</span><span class="pun">:</span><span class="com">//api.github.com/users/${user.name}`))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">githubUser </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let img </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'img'</span><span class="pun">);</span><span class="pln">
    img</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> githubUser</span><span class="pun">.</span><span class="pln">avatar_url</span><span class="pun">;</span><span class="pln">
    img</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"promise-avatar-example"</span><span class="pun">;</span><span class="pln">
    document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">img</span><span class="pun">);</span><span class="pln">

    setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      img</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">();</span><span class="pln">
      resolve</span><span class="pun">(</span><span class="pln">githubUser</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">},</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}))</span><span class="pln">
  </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">error </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">));</span><span class="pln"> </span><span class="com">// هنا</span></pre>

<p>
	الطبيعي هو ألّا يعمل تابِع <code>‎.catch</code> مطلقًا، ولكن لو رُفض أحد الوعود أعلاه (بسبب مشكلة في الشبكة أو كائن غير صالح أو أيّ شيء آخر)، فسيستلم التابِع الخطأ.
</p>

<h2>
	صياغة try..catch الضمنية
</h2>

<p>
	تُحيط بشيفرتي مُنفّذ الوعود ودوال مُعاملة الوعود عبارة <code>try..catch</code> مخفية إن صحّ القول، فإن حدث استثناء يُلتقط ويتعامل معه المحرّك على أنّه حالة رفض. خُذ مثلًا هذه الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1439_13" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// لاحِظ</span><span class="pln">
</span><span class="pun">}).</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Error: Whoops!</span></pre>

<p>
	… وهل تعمل تمامًا مثل عمل هذه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1439_15" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">));</span><span class="pln"> </span><span class="com">// لاحِظ</span><span class="pln">
</span><span class="pun">}).</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Error: Whoops!</span></pre>

<p>
	تتلقّى عبارة <code>try..catch</code> المخفي المُحيطة بالمُنفّذ الأخطاءَ تلقائيًا وتحوّلها إلى وعود مرفوضة.
</p>

<p>
	ولا يحدث هذا في دالة المُنفّذ فحسب بل أيضًا في دوال المُعاملة. فلو رمينا شيئًا في دالة المُعاملة للتابِع <code>throw</code> فيعني ذلك بأنّ الوعد رُفض وينتقل سير التحكّم إلى أقرب دالة تتعامل مع الأخطاء.
</p>

<p>
	إليك مثالًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1439_17" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  resolve</span><span class="pun">(</span><span class="str">"ok"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}).</span><span class="pln">then</span><span class="pun">((</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// نرفض الوعد</span><span class="pln">
</span><span class="pun">}).</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Error: Whoops!</span></pre>

<p>
	وهذا يحدث مع الأخطاء كافّة وليس فقط لتلك التي رُميت بعبارة <code>throw</code>. حتّى أخطاء المبرمجين، انظر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1439_19" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  resolve</span><span class="pun">(</span><span class="str">"ok"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}).</span><span class="pln">then</span><span class="pun">((</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  blabla</span><span class="pun">();</span><span class="pln"> </span><span class="com">// ما من دالة كهذه</span><span class="pln">
</span><span class="pun">}).</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ReferenceError: blabla is not defined</span></pre>

<p>
	تابِع <code>‎.catch</code> الأخير لا يستلم حالات الرفض الصريحة فحسب، بل تلك التي تحدث أحيانًا في دوال المُعاملة أعلاه أيضًا.
</p>

<h2>
	إعادة الرمي
</h2>

<p>
	كما رأينا فوجود تابِع <code>‎.catch</code> نهاية السلسلة شبيه بعبارة <code>try..catch</code>. يمكن أن نكتب ما نريد من دوال مُعاملة <code>‎.then</code> وثمّ استعمال تابِع <code>‎.catch</code> واحد في النهاية للتعامل مع أخطائها.
</p>

<p>
	في عبارات <code>try..catch</code> العاديّة نحلّل الخطأ ونُعيد رميه لو لم نستطع التعامل معه. ذات الأمر مع الوعود.
</p>

<p>
	فإن رمينا شيئًا داخل <code>‎.catch</code>، ينتقل سير التحكّم إلى أقرب دالة تتعامل مع الأخطاء. وإن تعاملنا مع الخطأ كما يجب فيتواصل إلى أقرب دالة <code>‎.then</code>.
</p>

<p>
	في المثال أسفله يتعامل التابِع <code>‎.catch</code> مع الخطأ كما ينبغي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1439_21" style="">
<span class="com">// ‫سلسلة التنفيذ: catch -&gt; then</span><span class="pln">
</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">);</span><span class="pln">

</span><span class="pun">}).</span><span class="kwd">catch</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  alert</span><span class="pun">(</span><span class="str">"The error is handled, continue normally"</span><span class="pun">);</span><span class="pln">

</span><span class="pun">}).</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">"Next successful handler runs"</span><span class="pun">));</span></pre>

<p>
	لا تنتهي هنا كتلة <code>‎.catch</code> البرمجية بأيّ أخطاء، بذلك تُستدعى دالة المُعاملة في تابِع <code>‎.then</code> التالي.
</p>

<p>
	نرى في المثال أسفله الحالة الثانية للتابِع <code>‎.catch</code>. تلتقط دالة المُعاملة عند <code>(*)</code> الخطأ ولكن لا تعرف التعامل معه (مثلًا لا تعرف إلّا أخطاء <code>URIError</code>) فترميه ثانيةً:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1439_23" style="">
<span class="com">// ‫سلسلة التنفيذ: catch ‎→ catch → then</span><span class="pln">
</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">);</span><span class="pln">

</span><span class="pun">}).</span><span class="kwd">catch</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error instanceof </span><span class="typ">URIError</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// handle it</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="str">"Can't handle such error"</span><span class="pun">);</span><span class="pln">

</span><span class="pun">*!*</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> error</span><span class="pun">;</span><span class="pln">  </span><span class="com">// ‫رمي هذا الخطأ أو أي خطأ آخر سينقُلنا إلى catch التالية</span><span class="pln">
</span><span class="pun">*/!*</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}).</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">/* لن يعمل هنا */</span><span class="pln">
</span><span class="pun">}).</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">error </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (**)</span><span class="pln">

  alert</span><span class="pun">(`</span><span class="typ">The</span><span class="pln"> unknown error has occurred</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">}`);</span><span class="pln">
  </span><span class="com">// لن يعيد أي شيء=&gt;عملية التنفيذ حدثت بطريقة عادية</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	ينتقل سير التنفيذ من تابِع <code>‎.catch</code> الأول عند <code>(*)</code> إلى التالي عند <code>(**)</code> في السلسلة.
</p>

<h2>
	حالات رفض
</h2>

<p>
	ماذا يحدث لو لم نتعامل مع الخطأ؟ فنقل مثلًا نسينا إضافة تابِع <code>‎.catch</code> في نهاية السلسلة هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1439_25" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  noSuchFunction</span><span class="pun">();</span><span class="pln"> </span><span class="com">// ‫خطأ (لا يوجد هذا التابع)</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// معالجات الوعد الناجح سواءً واحدة أو أكثر</span><span class="pln">
  </span><span class="pun">});</span><span class="pln"> 
</span><span class="com">// ‫بدون ‎.catch في النهاية!</span></pre>

<p>
	لو حدث خطأ يُرفض الوعد وعلى سير التنفيذ الانتقال إلى أقرب دالة تتعامل مع حالات الرفض. ولكن ما من دالة كهذه و”يعلق“ الخطأ إن صحّ التعبير إذ ما من شيفرة تتعامل معه.
</p>

<p>
	عمليًا يتشابه هذا مع الأخطاء التي لم نتعامل معها في الشيفرات، أي أنّ شيئًا مريعًا قد حدث.
</p>

<p>
	تتذكّر ما يحدث لو حدث خطأ عادي ولم تلتقطه عبارة <code>try..catch</code>؟ ”يموت“ النص البرمجي ويترك رسالةً في الطرفية. ذات الأمر يحدث مع حالات رفض الوعود التي لم يجري التعامل معها.
</p>

<p>
	يتعقّب محرّك جافاسكربت هذه الحالات ويولّد خطأً عموميًا في هذه الحالة. يمكنك أن تراه في الطرفية إن شغلت المثال أعلاه.
</p>

<p>
	يمكننا في المتصفّحات التقاط هذه الأخطاء باستعمال الحدث <code>unhandledrejection</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1439_27" style="">
<span class="pun">*!*</span><span class="pln">
window</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'unhandledrejection'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ‫لدى كائن الحدث خاصيتين مميزتين:</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">promise</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫[object Promise] - الوعد الذي يولد الخطأ</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">reason</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫Error: Whoops!‎ - كائن الخطأ غير المعالج</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
</span><span class="pun">*/!*</span><span class="pln">

</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">  </span><span class="com">// ‫لا يوجد catch لمعالجة الخطأ</span></pre>

<p>
	إنّما الحدث هو جزء من <a href="https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections" rel="external nofollow">معيار HTML</a>.
</p>

<p>
	لو حدث خطأ ولم يكن هناك تابِع <code>‎.catch</code> فيُشغّل معالج <code>unhandledrejection</code> ويحصل على كائن الحدث مع معلومات حول الخطأ حتى نتمكن من فعل شيء ما.
</p>

<p>
	مثل هذه الأخطاء عادةً ما تكون غير قابلة للاسترداد، لذلك أفضل طريقة للخروج هي إعلام المستخدم بالخطأ أو حتى إبلاغ الخادم بالخطأ.
</p>

<p>
	ثمّة في البيئات الأخرى غير المتصفّحات (مثل Node.js) طرائقَ أخرى لتعقّب الأخطاء التي لم نتعامل معها.
</p>

<h2>
	خلاصة
</h2>

<ul>
<li>
		يتعامل <code>‎.catch</code> مع الأخطاء في الوعود أيًا كانت: أكانت من استدعاءات <code>reject()‎</code> أو من رمي الأخطاء في دوال المُعاملة.
	</li>
	<li>
		يجب أن نضع <code>‎.catch</code> في الأماكن التي نريد أن نعالج الخطأ فيها ومعرفة كيفية التعامل معها. يجب على المعالج تحليل الأخطاء (الأصناف المخصصة تساعدنا بذلك) وإعادة رمي الأخطاء غير المعروفة (ربما تكون أخطاء برمجية).
	</li>
	<li>
		لا بأس بعدم استخدام <code>‎.catch</code> مطلقًا، إن لم يكُ هنالك طريقة للاسترداد من الخطأ.
	</li>
	<li>
		على أية حال، يجب أن يكون لدينا معالج الأحداث <code>unhandledrejection</code> (للمتصفحات وللبيئات الأخرى) وذلك لتتبع الأخطاء غير المُعالجة وإعلام المستخدم (وربما إعلام الخادم) عنها. حتى لا يموت تطبيقنا مطلقًا.
	</li>
</ul>
<h2>
	تمارين
</h2>

<h3>
	خطأ في تابِع setTimeout
</h3>

<p>
	ما رأيك هل ستعمل <code>.catch</code>؟ وضح إجابتك.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1439_29" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">},</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}).</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span></pre>

<p>
	الجواب: <strong>لا لن تعمل</strong>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1439_31" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">},</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}).</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span></pre>

<p>
	كما ذكرنا في هذا الفصل لدينا "صياغة <code>try..catch</code> ضمنية" حول تابع معيّن. لذلك تُعالج جميع الأخطاء المتزامنة.
</p>

<p>
	ولكن هنا الخطأ لا يُنشأ عندما يعمل المُنفذّ وإنما في وقت لاحق. لذا فإن الوعد لن يستطيع معالجته.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/promise-error-handling" rel="external nofollow">Error handling with promises</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">917</guid><pubDate>Fri, 19 Jun 2020 08:51:51 +0000</pubDate></item><item><title>&#x633;&#x644;&#x633;&#x644;&#x629; &#x627;&#x644;&#x648;&#x639;&#x648;&#x62F; Promises chaining &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promises-chaining-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r916/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/72.jpg.97a091c877201a61983df17fdfcee9f4.jpg" /></p>
<p>
	طرحناها في الفصل "<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%B1%D8%AF%D9%88%D8%AF-%D8%A7%D9%84%D9%86%D8%AF%D8%A7%D8%A1-callbacks-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r914/" rel="">مقدمة إلى ردود النداء callbacks</a>" مشكلةً ألا وهي أنّ لدينا تسلسلًا من المهام غير المتزامنة ويجب أن تُجرى واحدةً بعد الأخرى، مثلًا تحميل السكربتات. كيف نكتب شيفرة … لهذه المشكلة؟
</p>

<p>
	تقدّم لنا الوعود طرائق مختلفة لهذا الغرض. وفي هذا الفصل سنتكلّم عن سَلسلة الوعود فقط.
</p>

<p>
	هكذا تكون:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_7" style=""><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="lit">1</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">

</span><span class="pun">}).</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (**)</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> result </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">

</span><span class="pun">}).</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (***)</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 2</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> result </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">

</span><span class="pun">}).</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 4</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> result </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">

</span><span class="pun">});</span></pre>

<p>
	الفكرة وما فيها هي تمرير الناتج في سلسلة توابِع <code>‎.then</code> تابعًا تابعًا.
</p>

<p>
	هكذا تكون:
</p>

<ol>
	<li>
		يبدأ الوعد الأوّل ويُنجز خلال ثانية واحدة (*).
	</li>
	<li>
		بعدها يُستدعى معالج <code>‎.then</code> <code>(**)</code>.
	</li>
	<li>
		النتيجة التي ستعود ستمرر إلى معالج <code>‎.then</code> التالي <code>(***)</code>.
	</li>
	<li>
		وهكذا… .
	</li>
</ol>

<p>
	نظرًا لتمرير النتيجة على طول سلسلة المعالجات، يمكننا رؤية سلسلة من استدعاءات <code>alert</code> هكذا: 1 ← 2 ← 4.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="47724" href="https://academy.hsoub.com/uploads/monthly_2020_06/promise-then-chain.png.a9e42ccc2e0287de366efe6b7c75a755.png" rel="" data-fileext="png"><img alt="promise-then-chain.png" class="ipsImage ipsImage_thumbnailed" data-fileid="47724" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_06/promise-then-chain.thumb.png.0f3fe680563d97936150b0b4eb149d13.png"></a>
</p>

<p>
	ويعود سبب هذا كلّه إلى أنّ استدعاء <code>promise.then</code> يُعيد وعدًا هو الآخر، بذلك يمكننا استدعاء التابِع <code>‎.then</code> التالي على ذلك الوعد، وهكذا.
</p>

<p>
	حين تُعيد دالة المُعاملة قيمةً ما، تصير القيمة ناتج ذلك الوعد، بذلك يمكن استدعاء <code>‎.then</code> عليه.
</p>

<p>
	<strong>خطأ شائع بين المبتدئين: تقنيًا يمكننا إضافة أكثر من تابِع <code>‎.then</code> إلى وعد واحد. لا يُعدّ هذا سَلسلة وعود</strong>.
</p>

<p>
	مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_9" style=""><span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="lit">1</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> result </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> result </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> result </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	هنا كتبنا أكثر من دالة مُعاملة لوعد واحد، وهذه التوابِع لا تمرّر القيمة إلى بعضها البعض، بل كلّ تعالجها على حدة.
</p>

<p>
	إليك الصورة (ووازِن بينها وبين السلسلة أعلاه):
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="47725" href="https://academy.hsoub.com/uploads/monthly_2020_06/promise-then-many.png.17259aaac3a34cf266147d33fd5024c4.png" rel="" data-fileext="png"><img alt="promise-then-many.png" class="ipsImage ipsImage_thumbnailed" data-fileid="47725" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_06/promise-then-many.thumb.png.01269cdfb940b998f345a82040a51d95.png"></a>
</p>

<p>
	تتلقّى كلّ توابِع <code>‎.then</code> في نفس الوعد ذات الناتج (أي ناتج الوعد) بذلك تعرض الشيفرة أعلاه نتائج <code>alert</code> متطابقة: <code>1</code>.
</p>

<p>
	أمّا عمليًا فنادرًا ما نستعمل أكثر من دالة مُعاملة واحدة لكلّ وعد، على عكس السَلسلة التي يشيع استعمالها.
</p>

<h2>
	إعادة الوعود
</h2>

<p>
	يمكن لدالة المُعاملة (المستعملة في <code>‎.then(handler)‎</code>) إنشاء وعد وإعادته.
</p>

<p>
	هنا تنتظر دوال المُعاملة الأخرى حتّى يكتمل الوعد وتستلم ناتجه.
</p>

<p>
	مثال على هذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_11" style=""><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="lit">1</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">

</span><span class="pun">}).</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span><span class="pln">

  </span><span class="com">// لاحِظ</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">
    setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="pln">result </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

</span><span class="pun">}).</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (**)</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 2</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="pln">result </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

</span><span class="pun">}).</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 4</span><span class="pln">

</span><span class="pun">});</span></pre>

<p>
	هنا يعرض أوّل تابِع <code>.then</code> القيمة <code>1</code> ويُعيد <code>new Promise(…)‎</code> في السطر <code>(*)</code>. بعد ثانية واحدة، … الوعد ويُمرّر ناتجه (أي وسيط التابِع <code>resolve</code>، في حالتنا هو <code>result * 2</code>) إلى دالة المُعاملة التالية في تابِع <code>.then</code> التالي. نرى كيف أنّ الدالة في السطر <code>(**)</code> تعرض <code>2</code> وتؤدّي ما أدّته دالة المُعاملة السابقة.
</p>

<p>
	بذلك نحصل على ما حصلنا عليه في المثال السابق: 1 ثمّ 2 ثمّ 4، الفرق هو التأخير لمدّة ثانية بين كلّ استدعاء من استدعاءات <code>alert</code>.
</p>

<p>
	بإعادة الوعود يمكننا بناء سلسلة من الإجراءات غير المتزامنة.
</p>

<h2>
	مثال: loadScript
</h2>

<p>
	لنستعمل هذه الميزة مع دالة <code>loadScript</code> (التي كتبناها في الفصل السابق) لنُحمّل النصوص البرمجية واحدًا تلو الآخر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_13" style=""><span class="pln">loadScript</span><span class="pun">(</span><span class="str">"/article/promise-chaining/one.js"</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> loadScript</span><span class="pun">(</span><span class="str">"/article/promise-chaining/two.js"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> loadScript</span><span class="pun">(</span><span class="str">"/article/promise-chaining/three.js"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// نستعمل الدوال المعرّف عنها في النصوص البرمجية</span><span class="pln">
    </span><span class="com">// ونتأكّد تمامًا بأنّها حُمّلت</span><span class="pln">
    one</span><span class="pun">();</span><span class="pln">
    two</span><span class="pun">();</span><span class="pln">
    three</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">});</span></pre>

<p>
	يمكننا أيضًا تقصير الشيفرة قليلًا باستعمال الدوال السهميّة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_15" style=""><span class="pln">loadScript</span><span class="pun">(</span><span class="str">"/article/promise-chaining/one.js"</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">script </span><span class="pun">=&gt;</span><span class="pln"> loadScript</span><span class="pun">(</span><span class="str">"/article/promise-chaining/two.js"</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">script </span><span class="pun">=&gt;</span><span class="pln"> loadScript</span><span class="pun">(</span><span class="str">"/article/promise-chaining/three.js"</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">script </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// اكتمل تحميل النصوص، يمكننا استعمال الدوال فيها الآن</span><span class="pln">
    one</span><span class="pun">();</span><span class="pln">
    two</span><span class="pun">();</span><span class="pln">
    three</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">});</span></pre>

<p>
	نرى هنا أنّ كلّ استدعاء من استدعاءات <code>loadScript</code> يُعيد وعدًا، ويعمل تابِع <code>‎.then</code> التالي في السلسلة متى … الوعد. بعدها تبدأ الدالة بتحميل النص البرمجي التالي، وهكذا تُحمّل كلّ النصوص واحدًا بعد آخر.
</p>

<p>
	ويمكننا إضافة ما نريد من إجراءات غير متزامنة إلى السلسلة، ولن يزيد طول الشيفرة إلى اليمين، بل إلى أسفل، ولن نُقابل وجه … ثانيةً.
</p>

<p>
	يمكننا تقنيًا إضافة تابِع <code>‎.then</code> داخل دوال <code>loadScript</code> مباشرةً هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_17" style=""><span class="pln">loadScript</span><span class="pun">(</span><span class="str">"/article/promise-chaining/one.js"</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">script1 </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  loadScript</span><span class="pun">(</span><span class="str">"/article/promise-chaining/two.js"</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">script2 </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    loadScript</span><span class="pun">(</span><span class="str">"/article/promise-chaining/three.js"</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">script3 </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

      </span><span class="com">// ‫يمكن أن تصل هذه الدالة إلى المتغيّرات script1 وscript2 وscript3</span><span class="pln">
      one</span><span class="pun">();</span><span class="pln">
      two</span><span class="pun">();</span><span class="pln">
      three</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	وتؤدّي الشيفرة نفس العمل: تُحمّل 3 نصوص برمجية بالترتيب. المشكلة هي أنّ طولها يزيد نحو اليمين وهي نفس مشكلة ردود النداء.
</p>

<p>
	عادةً ما يجهل المبرمجون الجدد الذين يستعملون الوعود ميزة السَلسلة، فيكتبون الشيفرات هكذا. لكنّ سَلسلة الوعود هي الأمثل وغالبًا الأفضل.
</p>

<p>
	ولكنّ استعمال <code>.then</code> مباشرةً أحيانًا لا يكون بالمشكلة الكبيرة، إذ يمكن للدوال المتداخلة الوصول إلى … الخارجي. في المثال أعلاه مثلًا يمكن لآخر ردّ نداء متداخل الوصول إلى كلّ المتغيّرات <code>script1</code> و<code>script2</code> و<code>script3</code>، إلّا أنّ هذا استثناء عن القاعدة وليس قاعدة بحدّ ذاتها.
</p>

<p>
	<strong>ملاحظة</strong>: كائنات Thenables على وجه الدقة، لا تعيد المعالجات وعودًا وإنما تعيد كائن thenable - وهو كائن عشوائي له نفس توابع <code>.then</code>. ويتعامل معه بنفس طريقة التعامل مع الوعد.
</p>

<p>
	الفكرة أن مكتبات الخارجية تنفذ كائنات خاصة بها "متوافقة مع الوعد". ويمكن أن يملكوا مجموعة توابع موسّعة. ولكن يجب أن يتوافقوا مع الوعود الأصيلة، لأنهم ينفذون <code>.then</code>.
</p>

<p>
	وإليك مثالًا على كائن thenable:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_19" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">Thenable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">num</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">num </span><span class="pun">=</span><span class="pln"> num</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  then</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">);</span><span class="pln"> </span><span class="com">// function() { native code }</span><span class="pln">
    </span><span class="com">// ‫إنجاز الوعد وتحقيقه مع this.num*2 بعد ثانية</span><span class="pln">
    setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">num </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (**)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="lit">1</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">result </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Thenable</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">

  </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// shows 2 after 1000ms</span></pre>

<p>
	تتحقق جافاسكربت من الكائن المُعاد من معالج <code>.then</code> في السطر <code>(*)</code>: إن لديه تابع قابل للاستدعاء يدعى <code>then</code> عندها سيستدعي ذاك التابع مزودًا بذلك بالتوابع الأصيلة مثل: <code>resolve</code> و <code>reject</code> كوسطاء (مشابه للمنفذ) وينتظر حتى يستدعى واحدًا منهم. في المثال أعلاه تستدعى <code>resolve(2)</code> بعد ثانية انظر <code>(**)</code>. بعدها تمرر النتيجة إلى أسفل السلسلة.
</p>

<p>
	تتيح لنا هذه المميزات دمج الكائنات المخصصة مع سلاسل الوعود دون الحاجة إلى الوراثة من الوعد <code>Promise</code>.
</p>

<h2>
	مثال أضخم: fetch
</h2>

<p>
	عادةً ما نستعمل الوعود في برمجة الواجهات الرسومية لطلبات الشبكة. لنرى الآن مثالًا أوسع مجالًا قليلًا.
</p>

<p>
	سنستعمل التابِع <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-fetch-%D9%81%D9%8A-javascript-r739/" rel="">fetch</a> لتحميل بعض المعلومات التي تخصّ المستخدم من الخادوم البعيد. لهذا التابِع معاملات كثيرة اختيارية كتبنا عنا في فصول مختلفة، إلّا أنّ صياغته الأساسية بسيطة إلى حدّ ما:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_23" style=""><span class="pln">let promise </span><span class="pun">=</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">);</span></pre>

<p>
	هكذا نُرسل طلبًا شبكيًا إلى العنوان <code>url</code> ونستلم وعدًا يُحل مع قيمة الكائن <code>response</code> ما إن يردّ الخادم البعيد بترويسات الطلب، ولكن <em>قبل تنزيل الردّ كاملًا</em>.
</p>

<p>
	علينا استدعاء التابِع <code>response.text()‎</code> لقراءة الردّ كاملًا، وهو يُعيد وعدًا يُحل resolved متى نُزّل النص الكامل من الخادوم البعيد، وناتجه يكون ذلك النص.
</p>

<p>
	تُرسل الشيفرة أسفله طلبًا إلى <code>user.json</code> وتحمّل نصّه من الخادوم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_25" style=""><span class="pln">fetch</span><span class="pun">(</span><span class="str">'/article/promise-chaining/user.json'</span><span class="pun">)</span><span class="pln">
  </span><span class="com">// ‫إن ‎.then تعمل عندما يستجيب الخادم البعيد</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ‫إن التابع response.text()‎ يُعيد وعدًا جديدًا والذي يعاد مع كامل نص الاستجابة </span><span class="pln">
    </span><span class="com">// عندما يُحمّل</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">text</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">text</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ‫...وهنا سيكون محتوى الملف البعيد</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">text</span><span class="pun">);</span><span class="pln"> </span><span class="com">// {"name": "iliakan", isAdmin: true}</span><span class="pln">
  </span><span class="pun">});</span></pre>

<p>
	كما أنّ هناك التابِع <code>response.json()‎</code> والذي يقرأ البيانات المستلمة البعيدة ويحلّلها على أنّها JSON. في حالتنا هذا أفضل وأسهل فهيًا نستعمله.
</p>

<p>
	كما وسنستعمل الدوال السهميّة للاختصار قليلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_27" style=""><span class="com">// ‫مشابه للمثال أعلاه ولكن التابع response.json()‎ يحلل المحتوى البعيد كملف JSON</span><span class="pln">
fetch</span><span class="pun">(</span><span class="str">'/article/promise-chaining/user.json'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">user </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">));</span><span class="pln"> </span><span class="com">// iliakan, got user name</span></pre>

<p>
	الآن لنصنع شيئًا بهذا المستخدم الذي حمّلناه.
</p>

<p>
	يمكننا مثلًا إجراء طلبات أكثر من غِت‎هَب وتحميل ملف المستخدم الشخصي وعرض صورته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_29" style=""><span class="com">// ‫أنشئ طلب لـِuser.json</span><span class="pln">
fetch</span><span class="pun">(</span><span class="str">'/article/promise-chaining/user.json'</span><span class="pun">)</span><span class="pln">
  </span><span class="com">// ‫حمله وكأنه ملف json</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
  </span><span class="com">// ‫أنشئ طلب لـِ GitHub</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">user </span><span class="pun">=&gt;</span><span class="pln"> fetch</span><span class="pun">(`</span><span class="pln">https</span><span class="pun">:</span><span class="com">//api.github.com/users/${user.name}`))</span><span class="pln">
  </span><span class="com">// ‫حمّل الرد كملف json</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
  </span><span class="com">// ‫أظهر الصورة الرمزية (avatar) من (githubUser.avatar_url) لمدة 3 ثواني </span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">githubUser </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let img </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'img'</span><span class="pun">);</span><span class="pln">
    img</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> githubUser</span><span class="pun">.</span><span class="pln">avatar_url</span><span class="pun">;</span><span class="pln">
    img</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"promise-avatar-example"</span><span class="pun">;</span><span class="pln">
    document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">img</span><span class="pun">);</span><span class="pln">

    setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> img</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(),</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">
  </span><span class="pun">});</span></pre>

<p>
	الشيفرة تعمل على أكمل وجه (طالِع التعليقات لتعرف التفاصيل). ولكن هناك مشكلة فيه قد تحدث، وهي خطأ شائع يقع فيه من يستعمل الوعود أوّل مرّة.
</p>

<p>
	طالِع السطر <code>(*)</code>: كيف يمكن أن نفعل مهمّة معينة <em>متى</em> اكتمل عرض الصورة وأُزيلت؟ فلنقل مثلًا سنعرض استمارة لتحرير ذلك المستخدم أو أيّ شيء آخر. حاليًا… ذلك مستحيل.
</p>

<p>
	لنقدر على مواصلة السلسلة علينا إعادة وعد المُنجز متى اكتمل عرض الصورة.
</p>

<p>
	هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_31" style=""><span class="pln">fetch</span><span class="pun">(</span><span class="str">'/article/promise-chaining/user.json'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">user </span><span class="pun">=&gt;</span><span class="pln"> fetch</span><span class="pun">(`</span><span class="pln">https</span><span class="pun">:</span><span class="com">//api.github.com/users/${user.name}`))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
  </span><span class="com">// هنا</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">githubUser </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">
    let img </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'img'</span><span class="pun">);</span><span class="pln">
    img</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> githubUser</span><span class="pun">.</span><span class="pln">avatar_url</span><span class="pun">;</span><span class="pln">
    img</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"promise-avatar-example"</span><span class="pun">;</span><span class="pln">
    document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">img</span><span class="pun">);</span><span class="pln">

    setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      img</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">();</span><span class="pln">
      resolve</span><span class="pun">(</span><span class="pln">githubUser</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (**)</span><span class="pln">
    </span><span class="pun">},</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}))</span><span class="pln">
  </span><span class="com">// يحدث بعد 3 ثوانٍ</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">githubUser </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(`</span><span class="typ">Finished</span><span class="pln"> showing $</span><span class="pun">{</span><span class="pln">githubUser</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}`));</span></pre>

<p>
	هكذا صارت تُعيد دالة المُعاملة في <code>.then</code> عند السطر <code>(*)</code> كائنَ <code>new Promise</code> لا … إلّا بعد استدعاء <code>resolve(githubUser)‎</code> في <code>setTimeout</code> عند <code>(**)</code>.
</p>

<p>
	وسينتظر تابِع <code>‎.then</code> التالي في السلسلة اكتمال ذلك.
</p>

<p>
	تُعد إعادة الإجراءات غير المتزامنة للوعود دومًا إحدى الممارسات الصحيحة في البرمجة.
</p>

<p>
	هكذا يسهّل علينا التخطيط للإجراءات التي ستصير بعد هذا، فحتّى لو لم نريد توسعة السلسلة الآن لربّما احتجنا إلى ذلك لاحقًا.
</p>

<p>
	وأخيرًا، يمكننا أيضًا تقسيم الشيفرة إلى دوال يمكن إعادة استعمالها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_33" style=""><span class="kwd">function</span><span class="pln"> loadJson</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> loadGithubUser</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> fetch</span><span class="pun">(`</span><span class="pln">https</span><span class="pun">:</span><span class="com">//api.github.com/users/${name}`)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> showAvatar</span><span class="pun">(</span><span class="pln">githubUser</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let img </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'img'</span><span class="pun">);</span><span class="pln">
    img</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> githubUser</span><span class="pun">.</span><span class="pln">avatar_url</span><span class="pun">;</span><span class="pln">
    img</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"promise-avatar-example"</span><span class="pun">;</span><span class="pln">
    document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">img</span><span class="pun">);</span><span class="pln">

    setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      img</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">();</span><span class="pln">
      resolve</span><span class="pun">(</span><span class="pln">githubUser</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">},</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// نستعملها الآن:</span><span class="pln">
loadJson</span><span class="pun">(</span><span class="str">'/article/promise-chaining/user.json'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">user </span><span class="pun">=&gt;</span><span class="pln"> loadGithubUser</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">showAvatar</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">githubUser </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(`</span><span class="typ">Finished</span><span class="pln"> showing $</span><span class="pun">{</span><span class="pln">githubUser</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}`));</span><span class="pln">
 </span><span class="com">// اكتمل عرض كذا  // ...</span></pre>

<h2>
	خلاصة
</h2>

<p>
	إن أعادت دالة مُعاملة <code>‎.then</code> (أو <code>catch/finally</code>، لا يهمّ حقًا) وعدًا، فتنتظر بقية السلسلة حتّى تُنجز متى حدث ذلك يُمرّر الناتج (أو الخطأ) إلى التي بعدها.
</p>

<p>
	إليك الصورة الكاملة:
</p>

<p style="text-align: center;">
	<img alt="promise-handler-variants.png" class="ipsImage ipsImage_thumbnailed" data-fileid="82486" data-unique="h46pllmtm" src="https://academy.hsoub.com/uploads/monthly_2021_11/promise-handler-variants.png.3c10036a5217a60a8597aea50ba24a79.png">
</p>

<h2>
	تمارين
</h2>

<h3>
	الوعد: then وcatch
</h3>

<p>
	هل تؤدّي هاتين الشيفرتين نفس الغرض؟ أي هل يتطابق سلوكهما في الحالات المختلفة، وأيّما كانت دوال المُعاملة؟
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_35" style=""><span class="pln">promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">f1</span><span class="pun">).</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">f2</span><span class="pun">);</span></pre>

<p>
	مقابل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_37" style=""><span class="pln">promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">f1</span><span class="pun">,</span><span class="pln"> f2</span><span class="pun">);</span></pre>

<h4>
	الحل
</h4>

<p>
	الجواب المختصر: <strong>لا ليسا متساويين</strong>: الفرق أنه إن حدث خطأ في <code>f1</code> فستعالجها <code>‎.catch</code> هنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_39" style=""><span class="pln">promise
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">f1</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">f2</span><span class="pun">);</span></pre>

<p>
	…لكن ليس هنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6299_41" style=""><span class="pln">promise
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">f1</span><span class="pun">,</span><span class="pln"> f2</span><span class="pun">);</span></pre>

<p>
	وذلك بسبب تمرير الخطأ لأسفل السلسلة، وفي الجزء الثاني من الشيفرة لا يوجد سلسلة أقل من <code>f1</code>.
</p>

<p>
	بمعنى آخر يمرر <code>‎.then</code> النتيجة أو الخطأ إلى <code>‎.then/catch</code> التالية. لذا في المثال الأول يوجد <code>catch</code> بينما في المثال الثاني لا يوجد. ولذلك لم يعالج الخطأ.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/promise-chaining" rel="external nofollow">Promises chaining</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">916</guid><pubDate>Thu, 16 Jul 2020 18:04:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x648;&#x639;&#x648;&#x62F; Promise &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promise-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r915/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/71.jpg.a18ec9c2c4f13cb6576f45a92637b305.jpg" /></p>
<p>
	لنقل بأنّك أنت هو عبد الحليم حافظ، ولنفترض بأنّ مُعجبوك من المحيط إلى الخليج يسألونك ليلًا نهارًا عن الأغنية الشاعرية التالية.
</p>

<p>
	وكي تُريح بالك تعدهم بإرسالها إليهم ما إن تُنشر. فتُعطي مُعجبيك قائمة يملؤون فيها عناوين بريدهم. ومتى ما نشرت الأغنية يستلمها كلّ من في تلك القائمة. ولو حصل مكروه (لا سمح الله) مثل أن شبّت النار والتهمت الأستديو ولم تقدر على نشر الأغنية - لو حصل ذلك فسيعلمون به أيضًا.
</p>

<p>
	وعاش الجميع بسعادة وهناء: أنت إذ لا يُزعجك الجميع بالتهديدات والتوعّدات، ومُعجبيك إذ لن تفوتهم أيّة رائعة من روائعك الفنية.
</p>

<p>
	إليك ما يشبه الأمور التي نفعلها في الحياة الواقعية - في الحياة البرمجية:
</p>

<ol>
	<li>
		”شيفرة مُنتِجة“ تُنفّذ شيئًا وتأخذ الوقت. مثل الشيفرات التي تُحمّل البيانات عبر الشبكة. هذا أنت، ”المغنّي“.
	</li>
	<li>
		”شيفرة مُستهلِكة“ تطلب ناتج ”الشيفرة المُنتِجة“ ما إن يجهز. وهناك عديد من الدوال تحتاج إلى هذا الناتج. هذه ”مُعجبوك“.
	</li>
	<li>
		<em>الوعد</em> (Promise) هو كائن فريد في جافاسكربت يربط بين ”الشيفرة المُنتِجة“ و”الشيفرة المُستهلِكة“. في الحياة العملية، الوعد هو ”قائمة الاشتراك“. يمكن أن تأخذ ”الشيفرة المُنتِجة“ ما تلزم من وقت لتقدّم لنا النتيجة التي وعدتنا بها، وسيُجهّزها لنا ”الوعد“ لأيّة شيفرة طلبتها متى جهزت.
	</li>
</ol>

<p>
	إن هذه المقاربة ليست دقيقة جدًا على الرغم من أنها جيدة كبداية ولكن وعود جافاسكربت أكثر تعقيدًا من قائمة اشتراك بسيطة بل لديها ميزات وقيود إضافية.
</p>

<p>
	هذه صياغة الباني لكائنات الوعد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_7" style=""><span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ‫المُنفِّذ (الشيفرة المُنتجة، مثل ”المغنّي“)</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تُدعى الدالة الممرّرة إلى <code>new Promise</code> ”بالمُنفِّذ“. متى صُنع الوعد <code>new Promise</code> عملت الدالة تلقائيًا. يحتوي هذا المُنفِّذ الشيفرة المُنتجِة، ويمكن أن تقدّم لنا في النهاية ناتجًا. في مثالنا أعلاه، فالمُنفِّذ هذا هو ”المغنّي“.
</p>

<p>
	تقدّم جافاسكربت الوسيطين <code>resolve</code> و <code>reject</code> وهما ردود نداء. كما ولا نضع الشيفرة التي نريد تنفيذها إلا داخل المُنفِّذ.
</p>

<p>
	لا يهمّنا متى سيعرف المُنفِّذ الناتجَ (آجلًا كان ذلك أم عاجلًا)، بل أنّ عليه نداء واحدًا من ردود النداء هذه:
</p>

<ul>
	<li>
		<code>resolve(value)‎</code>: لو اكتملت المهمّة بنجاح. القيمة تسجّل في <code>value</code>.
	</li>
	<li>
		<code>reject(error)‎</code>: لو حدث خطأ. <code>error</code> هو كائن الخطأ.
	</li>
</ul>

<p>
	إذًا نُلخّص: يعمل المُنفِّذ تلقائيًا وعليه مهمّة استدعاء <code>resolve</code> أو <code>reject</code>.
</p>

<p>
	لكائن الوعد <code>promise</code> الذي أعاده الباني <code>new Promise</code> خاصيتين داخليتين:
</p>

<ul>
	<li>
		الحالة <code>state</code>: تبدأ بالقيمة <code>"pending"</code> وبعدها تنتقل إلى <code>"fulfilled"</code> متى استُدعت <code>resolve</code>، أو إلى <code>"rejected"</code> متى استُدعت <code>reject</code>.
	</li>
	<li>
		الناتج <code>result</code>: يبدأ أولًا غير معرّف <code>undefined</code>، وبعدها يتغيّر إلى <code>value</code> متى استُدعت <code>resolve(value)‎</code> أو يتغيّر إلى <code>error</code> متى استُدعت <code>reject(error)‎</code>.
	</li>
</ul>

<p>
	وفي النهاية ينقل المُنفِّذ الوعدَ <code>promise</code> ليصير بإحدى الحالات الآتية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="47718" href="https://academy.hsoub.com/uploads/monthly_2020_06/promise-resolve-reject.png.019e20ee123ef136f9d49ba0fec3f61e.png" rel=""><img alt="promise-resolve-reject.png" class="ipsImage ipsImage_thumbnailed" data-fileid="47718" data-unique="0qhwfnmyu" src="https://academy.hsoub.com/uploads/monthly_2020_06/promise-resolve-reject.thumb.png.0e30c1ea43d02e95692e33935469fca2.png"></a>
</p>

<p>
	سنرى لاحقًا كيف سيشترك ”مُعجبونا“ بهذه التغييرات.
</p>

<p>
	إليك مثالًا عن بانيًا للوعود ودالة مُنفِّذ بسيطة فيها ”شيفرة مُنتجِة“ تأخذ بعض الوقت (باستعمال <code>setTimeout</code><span class="ipsEmoji">?</span>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_9" style=""><span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// تُنفّ الدالة مباشرةً ما إن يُصنع الوعد</span><span class="pln">

  </span><span class="com">// ‫وبعد ثانية واحدة نبعث بإشارة بأنّ المهمة انتهت والنتيجة هي ”تمت“ (done)</span><span class="pln">
  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="str">"done"</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	بتشغيل الشيفرة أعلاه، نرى أمرين اثنين:
</p>

<ol>
	<li>
		يُستدعى المُنفِّذ تلقائيًا ومباشرةً (عند استعمال <code>new Promise</code>).
	</li>
	<li>
		يستلم المُنفِّذ وسيطين: دالة الحلّ <code>resolve</code> ودالة الرفض <code>reject</code>، وهي دوال معرّفة مسبقًا في محرّك جافاسكربت، ولا داعٍ بأن نصنعها نحن، بل استدعاء واحدة ما إن تجهز النتيجة.
	</li>
</ol>

<p>
	بعد سنة من عملية ”المعالجة“ يستدعي المُنفِّذ الدالةَ <code>resolve("done")‎</code> لتُنتج الناتج. هكذا تتغيّر حالة كائن <code>promise</code>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="47717" href="https://academy.hsoub.com/uploads/monthly_2020_06/promise-resolve-1.png.f729e054303ff46f738a49578e8024ef.png" rel=""><img alt="promise-resolve-1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="47717" data-unique="a0yaf2rde" src="https://academy.hsoub.com/uploads/monthly_2020_06/promise-resolve-1.thumb.png.39c0117d7423fcc82ab06b0530b5a067.png"></a>
</p>

<p>
	كان هذا مثالًا عن مهمّة اكتملت بنجاح، أو ”وعد تحقّق“.
</p>

<p>
	والآن سنرى مثالًا عن مُنفِّذ يرفض الوعد مُعيدًا خطأً:
</p>

<pre class="ipsCode">let promise = new Promise(function(resolve, reject) {
  // بعد ثانية واحدة نبعث بإشارة بأنّ المهمة انتهت ونُعيد خطأً
  setTimeout(() =&gt; reject(new Error("Whoops!")), 1000);
});
</pre>

<p>
	باستدعاء <code>reject(...)‎</code> ننقل حالة كائن الوعد إلى حالة الرفض <code>"rejected"</code>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="47720" href="https://academy.hsoub.com/uploads/monthly_2020_06/promise-reject-1.png.feb835a477f0f8a8501ba0035d821a23.png" rel=""><img alt="promise-reject-1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="47720" data-unique="4iv54tddu" src="https://academy.hsoub.com/uploads/monthly_2020_06/promise-reject-1.thumb.png.4832529414763ffbb94168a167917aad.png"></a>
</p>

<p>
	ملخّص القول هو أنّ على المُنفِّذ تنفيذ المهمة (أي ما يأخذ بعض الوقت ليكتمل) وثمّ يستدعي واحدةً من الدالتين <code>resolve</code> أو <code>reject</code> لتغيير حالة كائن الوعد المرتبط بالمُنفِّذ.
</p>

<p>
	يُسمّى الوعد الذي تحقّق أو نُكث الوعد المنُجز، على العكس من الوعد المعلّق.
</p>

<p>
	<strong>ملاحظة</strong>: إما أن تظهر نتيجة واحدة أو خطأ، يجب على المنفّذ أن يستدعي إما <code>resolve</code> أو <code>reject</code>. أي تغيير في الحالة يعدّ تغييرًا نهائيًا.
</p>

<p>
	وسيُتجاهل جميع الاستدعاءات اللاحقة سواءً أكانت <code>resolve</code> أو <code>reject</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_11" style=""><span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  resolve</span><span class="pun">(</span><span class="str">"done"</span><span class="pun">);</span><span class="pln">

  reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"…"</span><span class="pun">));</span><span class="pln"> </span><span class="com">// ستتجاهل</span><span class="pln">
  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="str">"…"</span><span class="pun">));</span><span class="pln"> </span><span class="com">// ستتجاهل</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	الفكرة هنا أن خرج عمل المنفذّ سيعرض إما نتيجة معينة أو خطأ.
</p>

<p>
	وتتوقع التعليمتين <code>resolve</code>/<code>reject</code> وسيطًا واحدًا مُررًا (أو بدون وسطاء نهائيًا) وأي وسطاء إضافية ستُتجاهل.
</p>

<p>
	<strong>ملاحظة</strong>: الرفض مع كائن <code>Error</code> في حال حدوث خطأ ما، يجب على المنفذّ أن يستدعي تعليمة <code>reject</code>. ويمكن تمرير أي نوع من الوسطاء (تمامًا مثل: <code>resolve</code>). ولكن يوصى باستخدام كائنات <code>Error</code> (أو أي كائنات ترث من <code>Error</code>). وقريبًا سنعرف بوضوح سبب ذلك.
</p>

<p>
	<strong>ملاحظة</strong>: استدعاء <code>resolve</code>/<code>reject</code> الفوري عمليًا عادة ينجز المنفذّ عمله بشكل متزامن ويستدعي <code>resolve</code>/<code>reject</code> بعد مرور بعض الوقت، ولكن الأمر ليس إلزاميًا، يمكننا استدعاء <code>resolve</code> أو <code>reject</code> فورًا، هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_13" style=""><span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// يمكننا القيام بالمهمة مباشرة</span><span class="pln">
  resolve</span><span class="pun">(</span><span class="lit">123</span><span class="pun">);</span><span class="pln"> </span><span class="com">// أظهر مباشرة النتيجة: 123</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	على سبيل المثال من الممكن أن يحدث ذلك في حال البدء بمهمة معينة ولكن تكتشف بأن كلّ شيء أنجز وخزّن في الذاكرة المؤقتة.
</p>

<p>
	هذا جيد، فعندها يجب أن ننجز الوعد فورًا.
</p>

<p>
	<strong>ملاحظة</strong>: الحالة <code>state</code> و النتيجة <code>result</code> الداخليتين تكون خصائص الحالة <code>state</code> و النتيجة <code>result</code> لكائن الوعد داخلية. ولا يمكننا الوصول إليهم مباشرة. يمكننا استخدام التوابِع <code>‎.then</code>/<code>.catch</code>/<code>.finally</code> لذلك والتي سنشرحُها أدناه.
</p>

<h2>
	الاستهلاك: عبارات then وcatch وfinally
</h2>

<p>
	كائن الوعد هو كالوصلة بين المُنفِّذ (أي ”الشيفرة المُنتِجة“ أو ”المغنّي“) والدوال المُستهلكة (أي ”المُعجبون“) التي ستسلم الناتج أو الخطأ. يمكن تسجيل دوال الاستهلاك (أو أن تشترك، كما في المثال العملي ذاك) باستعمال التوابِع <code>‎.then</code> و<code>‎.catch</code> و<code>‎.finally</code>.
</p>

<h3>
	then
</h3>

<p>
	يُعدّ <code>‎.then</code> أهمّها وعِماد القصة كلها. صياغته هي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_15" style=""><span class="pln">promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">
  </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">/* نتعامل مع الناتج الصحيح */</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">/* نتعامل مع الخطأ */</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	الوسيط الأوّل من التابِع <code>‎.then</code> يُعدّ دالة تُشغّل إن تحقّق الوعد، ويكون الوسيطُ الناتج.
</p>

<p>
	بينما الوسيط الثاني يُعدّ دالةً تُشغّل إن رُفض الوعد، ويكون الوسيطُ الخطأ.
</p>

<p>
	إليك مثال نتعامل فيه مع وعد تحقّق بنجاح:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_22" style=""><span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="str">"done!"</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="com">// ‫تُنفِّذ resolve أول دالة في ‎.then</span><span class="pln">
promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">

  result </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">),</span><span class="pln"> </span><span class="com">// ‫ إظهار "done!‎" بعد ثانية</span><span class="pln">

  error </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="com">// هذا لا يعمل</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	هكذا نرى الدالة الأولى هي التي نُفّذت.
</p>

<p>
	وإليك المثال في حالة الرفض:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_24" style=""><span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">)),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="com">// ‫تُنفِّذ reject ثاني دالة في ‎.then</span><span class="pln">
promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">
  result </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">),</span><span class="pln"> </span><span class="com">// لا تعمل</span><span class="pln">

  error </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="com">// ‫ إظهار "Error: Whoops!‎" بعد ثانية</span><span class="pln">

</span><span class="pun">);</span></pre>

<p>
	لو لم نُرِد إلّا حالات الانتهاء الناجحة، فيمكن أن نقدّم دالةً واحدة وسيطًا إلى <code>‎.then</code> فقط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_26" style=""><span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="str">"done!"</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">


promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫ إظهار "done!‎" بعد ثانية</span></pre>

<h3>
	catch
</h3>

<p>
	لو لم نكن نهتمّ إلّا بالأخطاء، فعلينا استعمال <code>null</code> وسيطًا أولًا: <code>‎.then(null, errorHandlingFunction)‎</code>، أو نستعمل <code>‎.catch(errorHandlingFunction)‎</code> وهو يؤدّي ذات المبدأ تمامًا ولا فرق إلّا قصر الثانية مقارنة بالأولى:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_28" style=""><span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">)),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">


</span><span class="com">// .catch(f) is the same as promise.then(null, f)</span><span class="pln">
promise</span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫إظهار "Error: Whoops!‎" بعد ثانية</span></pre>

<h3>
	finally
</h3>

<p>
	كما المُنغلِقة <code>finally</code> في عبارات <code>try {...} catch {...}‎</code> العادية، فهناك مثلها في الوعود.
</p>

<p>
	استدعاء <code>‎.finally(f)‎</code> يشبه استدعاء <code>‎.then(f, f)‎</code>، ووجه الشبه هو أنّ الدالة <code>f</code> تعمل دومًا متى استقر الوعد أو أنجز سواءً كان قد تحقّق أو نُكث.
</p>

<p>
	استعمال <code>finally</code> مفيد جدًا لتنظيف ما تبقّى من أمور مهمًا كان ناتج الوعد، مثل إيقاف أيقونات التحميل (فلم نعد نحتاجها). هكذا مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_30" style=""><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ‫افعل شيئًا يستغرق وقتًا ثم استدع resolve/reject lre </span><span class="pln">
</span><span class="pun">})</span><span class="pln">

  </span><span class="com">// runs when the promise is settled, doesn't matter successfully or not</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">finally</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> stop loading indicator</span><span class="pun">)</span><span class="pln">

  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">result </span><span class="pun">=&gt;</span><span class="pln"> show result</span><span class="pun">,</span><span class="pln"> err </span><span class="pun">=&gt;</span><span class="pln"> show error</span><span class="pun">)</span></pre>

<p>
	ولكنها ليست متطابقة تمامًا مع <code>then(f,f)‎</code>، فهناك فروقات مهمّة:
</p>

<p>
	أولًا، ليس لدالة المُعالجة <code>finally</code> أيّ وسطاء. أي لسنا نعلم في <code>finally</code> أكان الوعد تحقّق أو نُكث، وهذه ليست مشكلة إذ ما نريده عادةً هو تنفيذ بعض الأمور ”العامّة“ لنُنهي ما بدأنا به.
</p>

<p>
	ثانيًا، يمرُّ مُعالج <code>finally</code> على النتائج والأخطاء وبعدها إلى المعالج التالي. مثال على ذلك هو الناتج الذي تمرّر من <code>finally</code> إلى <code>then</code> هنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_32" style=""><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="str">"result"</span><span class="pun">),</span><span class="pln"> </span><span class="lit">2000</span><span class="pun">)</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">finally</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">"Promise ready"</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">result </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">));</span><span class="pln"> </span><span class="com">// &lt;-- ‫‎.then ستعالج الناتج</span></pre>

<p>
	كما ترى القيمة <code>value</code> المعادة من الوعد تُمرر عبر <code>finally</code> إلى <code>then</code>، وهذا السلوك مفيد جدًا إذ لا يفترض بأن تتعامل <code>finally</code> مع ناتج الوعد، بل تمرّره إلى من يتعامل معه، وهي مخصصة إلى إجراء عمليات ختامية معينة (مثل التنظيف) بغض النظر عن الناتج.
</p>

<p>
	وهنا واجه الوعد خطأً، وتمرّر من <code>finally</code> إلى <code>catch</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_34" style=""><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"error"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">finally</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">"Promise ready"</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">err</span><span class="pun">));</span><span class="pln">  </span><span class="com">// &lt;-- ‫‎.catch ستعالج كائن الخطأ error object</span></pre>

<p>
	ثالثًا، معالج finally لا يجب أن يعيد شيئًا، وإن فعل، فسيجري تجاهل القيمة المعادة، والاستثناء الوحيد هنا هو عندما يرمي معالج finally خطأً ويُمرر آنذاك الخطأ إلى المعالج التالي بغض النظر عما سبقه من مخرجات.
</p>

<p>
	الخلاصة، المعالج finally:
</p>

<ul>
	<li>
		لا يأخذ أي مخرجات من المعالج السابق، فليس لديه معاملات، بل تتخطاه المخرجات إلى المعالج التالي المناسب إن وُجد.
	</li>
	<li>
		لا يجب أن يعيد شيئًا وإن أعاد فلن يحصل شيء وتُتجاهل القيمة.
	</li>
	<li>
		إن رمى خطأً، فسيستلمه أقرب معالج أخطاء.
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: في الوعود المنجزة المُعالجات تعمل مباشرة إن كان الوعد مُعلقًا لسببٍ ما، فإن معالجات <code>‎.then/catch/finally</code> ستنتظره. عدا ذلك (إن كان الوعد مُنجزًا) فإن المعالجات ستنفذّ مباشرةً:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_36" style=""><span class="com">// يصبح الوعد منجزًا ومتحققًا بعد الإنشاء مباشرةً</span><span class="pln">
let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="str">"done!"</span><span class="pun">));</span><span class="pln">

promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span><span class="pln"> </span><span class="com">// done! (تظهر الآن)</span></pre>

<p>
	الآن لنرى أمثلة عملية على فائدة الوعود في كتابة الشيفرات غير المتزامنة.
</p>

<h2>
	تحميل السكربتات: الدالة loadScript
</h2>

<p>
	أمامنا من الفصل الماضي الدالة <code>loadScript</code> لتحميل السكربتات.
</p>

<p>
	إليك الدالة بطريقة ردود النداء، لنتذكّرها لا أكثر ولا أقل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_38" style=""><span class="kwd">function</span><span class="pln"> loadScript</span><span class="pun">(</span><span class="pln">src</span><span class="pun">,</span><span class="pln"> callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let script </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'script'</span><span class="pun">);</span><span class="pln">
  script</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> src</span><span class="pun">;</span><span class="pln">

  script</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> callback</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> script</span><span class="pun">);</span><span class="pln">
  </span><span class="com">// خطأ في تحميل السكربت كذا</span><span class="pln">
  script</span><span class="pun">.</span><span class="pln">onerror </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> callback</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(`</span><span class="typ">Script</span><span class="pln"> load error </span><span class="kwd">for</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">src</span><span class="pun">}`));</span><span class="pln">

  document</span><span class="pun">.</span><span class="pln">head</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هيًا نُعد كتابتها باستعمال الوعود.
</p>

<p>
	لن تطلب دالة <code>loadScript</code> الجديدة أيّ ردود نداء، بل ستصنع كائن وعد يتحقّق متى اكتمل التحميل، وتُعيده. يمكن للشيفرة الخارجية إضافة الدوال المُعالجة (أي دوال الاشتراك) إليها باستعمال <code>.then</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_40" style=""><span class="kwd">function</span><span class="pln"> loadScript</span><span class="pun">(</span><span class="pln">src</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let script </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'script'</span><span class="pun">);</span><span class="pln">
    script</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> src</span><span class="pun">;</span><span class="pln">

    script</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">
    script</span><span class="pun">.</span><span class="pln">onerror </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(`</span><span class="typ">Script</span><span class="pln"> load error </span><span class="kwd">for</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">src</span><span class="pun">}`));</span><span class="pln">

    document</span><span class="pun">.</span><span class="pln">head</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الاستعمال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_42" style=""><span class="pln">let promise </span><span class="pun">=</span><span class="pln"> loadScript</span><span class="pun">(</span><span class="str">"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"</span><span class="pun">);</span><span class="pln">

promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">
  script </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">script</span><span class="pun">.</span><span class="pln">src</span><span class="pun">}</span><span class="pln"> is loaded</span><span class="pun">!`),</span><span class="pln">
  error </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(`</span><span class="typ">Error</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`)</span><span class="pln">
</span><span class="pun">);</span><span class="pln">

promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">script </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">'Another handler...'</span><span class="pun">));</span></pre>

<p>
	بنظرة خاطفة يمكن أن نرى فوائد هذه الطريقة موازنةً بطريقة ردود النداء:
</p>

<table>
	<thead>
		<tr>
			<th>
				الوعود
			</th>
			<th>
				ردود النداء
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				تتيح لنا الوعود تنفيذ الأمور بترتيبها الطبيعي أولًا نشغّل <code>loadScript(script)‎</code> ومن بعدها <code>‎.then</code> نكتب ما نريد فعله بالنتيجة.
			</td>
			<td>
				يجب أن يكون تابِع <code>callback</code> تحت تصرفنا عند استدعاء <code>loadScript(script, callback)‎</code>. بعبارة أخرى يجب أن نعرف ما سنفعله بالنتيجة <strong>قبل</strong> استدعاء <code>loadScript</code>.
			</td>
		</tr>
		<tr>
			<td>
				يمكننا استدعاء <code>‎.then</code> في الوعد عدة مرات كما نريد. في كلّ مرة نضيف معجب جديدة "fan"، هنالك تابع سيضيف مشتركين جُدد إلى قائمة المشتركين. سنرى المزيد حول هذا الأمر في الفصل القادم
			</td>
			<td>
				يمكن أن يكون هنالك ردّ واحد فقط.
			</td>
		</tr>
	</tbody>
</table>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
}

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<p>
	إذًا، فالوعود تقدّم لنا تحكمًا مرنًا بالشيفرة وسير تنفيذها، وما زالت هنالك الكثير من الأمور الرائعة التي سنتعرف عليها الفصل القادم.
</p>

<h2>
	تمارين
</h2>

<h3>
	إعادة … الوعد؟
</h3>

<p>
	ما ناتج الشيفرة أدناه؟
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_44" style=""><span class="pln">let promise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  resolve</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">

  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="lit">2</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

promise</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">alert</span><span class="pun">);</span></pre>

<h4>
	الحل
</h4>

<p>
	الناتج هو: <code>1</code>.
</p>

<p>
	يُهمل استدعاء <code>resolve</code> الثاني إذ لا يتهمّ المحرّك إلّا بأول استدعاء من <code>reject/resolve</code>، والباقي كلّه يُهمل.
</p>

<h3>
	التأخير باستعمال الوعود
</h3>

<p>
	تستعمل الدالة المضمّنة في اللغة <code>setTimeout</code> ردودَ النداء. اصنع واحدة تستعمل الوعود.
</p>

<p>
	على الدالة <code>delay(ms)‎</code> إعادة وعد ويجب أن … هذا الوعد خلال <code>ms</code> مليثانية، ونُضيف تابِع <code>.then</code> إليه هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_46" style=""><span class="kwd">function</span><span class="pln"> delay</span><span class="pun">(</span><span class="pln">ms</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// شيفرتك هنا</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

delay</span><span class="pun">(</span><span class="lit">3000</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">'runs after 3 seconds'</span><span class="pun">));</span></pre>

<h4>
	الحل
</h4>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_48" style=""><span class="kwd">function</span><span class="pln"> delay</span><span class="pun">(</span><span class="pln">ms</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> setTimeout</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> ms</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

delay</span><span class="pun">(</span><span class="lit">3000</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">'runs after 3 seconds'</span><span class="pun">));</span></pre>

<p>
	لاحظ أنّنا في هذا التمرين استدعينا <code>resolve</code> بلا وسطاء، ولم نُعد أيّ قيمة من <code>delay</code> بل … فقط
</p>

<h3>
	صورة دائرة متحركة مع وعد
</h3>

<p>
	أعِد كتابة الدالة <code>showCircle</code> في حلّ التمرين <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%B1%D8%AF%D9%88%D8%AF-%D8%A7%D9%84%D9%86%D8%AF%D8%A7%D8%A1-callbacks-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r914/" rel="">السابق</a> لتُعيد وعدًا بدل أن تستلم ردّ نداء. ويكون استعمالها الجديد هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_278_50" style=""><span class="pln">showCircle</span><span class="pun">(</span><span class="lit">150</span><span class="pun">,</span><span class="pln"> </span><span class="lit">150</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">div </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  div</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">'message-ball'</span><span class="pun">);</span><span class="pln">
  div</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="str">"Hello, world!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	ليكن الحلّ في التمرين المذكور أساس المسألة الآن.
</p>

<h4>
	الحل
</h4>

<p>
	يمكنك مشاهدة الحل عبر <a href="https://plnkr.co/edit/Q1jyGXvy9INMRG3Y?p=preview" rel="external nofollow">المثال الحي</a>.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/promise-basics" rel="external nofollow">Promise</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">915</guid><pubDate>Mon, 13 Jul 2020 18:08:00 +0000</pubDate></item><item><title>&#x645;&#x642;&#x62F;&#x645;&#x629; &#x625;&#x644;&#x649; &#x631;&#x62F;&#x648;&#x62F; &#x627;&#x644;&#x646;&#x62F;&#x627;&#x621; callbacks &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%B1%D8%AF%D9%88%D8%AF-%D8%A7%D9%84%D9%86%D8%AF%D8%A7%D8%A1-callbacks-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r914/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/70.jpg.2d111467aca7e6404f1819d2a60be3e7.jpg" /></p>

<p>
	<strong>ملاحظة ابتدائية</strong>: لتوضيح طريقة استخدام ردود النداء callbacks والوعود promises والمفاهيم المجردة سنستخدم بعض توابِع المتصفح، تحديدًا سكربتات التحميل loading scripts وأدوات التلاعب بالمستندات البسيطة.
</p>

<p>
	إن لم تكُ على دراية بهذه الطرق، وكان استخدامها في الأمثلة مربكًا نوعًا ما، أو حتى إن رغبتَ فقط في فهمها فهمًا أفضل، فيمكنك قراءة بعض الفصول من <a href="https://javascript.info/document" rel="external nofollow">الجزء التالي</a> من الدورة التعليمية.
</p>

<p>
	وبالرغم من ذلك سنحاول توضيح الأمور بكل الأحوال ولن يكون هنالك شيء معقد في المتصفح.
</p>

<p>
	أغلب الإجراءات في جافاسكربت هي إجراءات <em>غير متزامنة</em>، أي أنّنا نشغّلها الآن ولكنّها تنتهي في وقت لاحق.
</p>

<p>
	مثال على ذلك هو حين نُجدول تلك الإجراءات باستعمال <code>setTimeout</code>.
</p>

<p>
	يُعدّ تحميل السكربتات والوحدات (سنشرحها في الفصول القادمة) أمثلة فعلية عن الإجراءات غير المتزامنة.
</p>

<p>
	لاحظ مثلًا الدالة <code>loadScript(src)‎</code> أسفله، إذ تُحمّل سكربتًا من العنوان <code>src</code> الممرّر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_7" style="">
<span class="kwd">function</span><span class="pln"> loadScript</span><span class="pun">(</span><span class="pln">src</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="com">// ‫أنشئ وسم &lt;script&gt; وأضفه للصفحة</span><span class="pln">
</span><span class="com">// ‫فسيؤدي ذلك إلى بدء تحميل السكربت ذي الخاصية src ثم تنفيذه عند الاكتمال</span><span class="pln">
  let script </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'script'</span><span class="pun">);</span><span class="pln">
  script</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> src</span><span class="pun">;</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">head</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُضيف الدالة إلى المستند الوسم <code>&lt;script src=‎"…"‎&gt;</code> الجديد الذي وُلّد ديناميكيًا. حين يعمل المتصفّح ينفّذ الدالة.
</p>

<p>
	يمكننا استعمال هذه الدالة هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_13" style="">
<span class="com">// حمّل ونفّذ السكربت في هذا المكان</span><span class="pln">
loadScript</span><span class="pun">(</span><span class="str">'/my/script.js'</span><span class="pun">);</span></pre>

<p>
	يُنفّذ هذا السكربت ”بلا تزامن“ إذ تحميله يبدأ الآن أمّا تشغيله فيكون لاحقًا متى انتهى الدالة.
</p>

<p>
	ولو كانت هناك شيفرة أسفل الدالة <code>loadScript(…)‎</code> فلن ننتظر انتهاء تحميل السكربت.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_9" style="">
<span class="pln">loadScript</span><span class="pun">(</span><span class="str">'/my/script.js'</span><span class="pun">);</span><span class="pln">
</span><span class="com">// الشيفرة أسفل ‫loadScript</span><span class="pln">
</span><span class="com">// لا تنتظر انتهاء تحميل السكربت</span><span class="pln">
</span><span class="com">// ...</span></pre>

<p>
	فنقل بأنّنا نريد استعمال السكربت متى انتهى تحميله (فهو مثلًا يُصرّح عن دوال جديدة ونريد تشغيلها).
</p>

<p>
	ولكن لو حدث ذلك مباشرةً بعد استدعاء <code>loadScript(…)‎</code>، فلن تعمل الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_11" style="">
<span class="com">// في السكربت الدالة ‫"function newFunction() {…}‎"</span><span class="pln">
loadScript</span><span class="pun">(</span><span class="str">'/my/script.js'</span><span class="pun">);</span><span class="pln"> 

newFunction</span><span class="pun">();</span><span class="pln"> </span><span class="com">// ما من دالة بهذا الاسم!</span></pre>

<p>
	الطبيعي هو أنّ ليس على المتصفّح انتظار مدّة من الزمن حتّى ينتهي تحميل السكربت. كما نرى فحاليًا لا تقدّم لنا الدالة <code>loadScript</code> أيّ طريقة لنعرف وقت اكتمال التحميل، بل يُحمّل السكربت ويُشغّل متى اكتمل، وفقط. ولكن ما نريده هو معرفة وقت ذلك كي نستعمل الدوال والمتغيرات الجديدة فيه.
</p>

<p>
	لنُضيف دالة ردّ نداء <code>callback</code> لتكون الوسيط الثاني لدالة <code>loadScript</code>، كي تُنفّذ متى انتهى تحميل السكربت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_15" style="">
<span class="kwd">function</span><span class="pln"> loadScript</span><span class="pun">(</span><span class="pln">src</span><span class="pun">,</span><span class="pln"> callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let script </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'script'</span><span class="pun">);</span><span class="pln">
  script</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> src</span><span class="pun">;</span><span class="pln">

  script</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> callback</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">

  document</span><span class="pun">.</span><span class="pln">head</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الآن متى أردنا استدعاء الدوال الجديدة في السكربت، نكتبها في دالة ردّ النداء تلك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_17" style="">
<span class="pln">loadScript</span><span class="pun">(</span><span class="str">'/my/script.js'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// يعمل ردّ النداء بعد تحميل السكربت</span><span class="pln">
  newFunction</span><span class="pun">();</span><span class="pln"> </span><span class="com">// الآن تعمل</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	هذه الفكرة تمامًا: يُعدّ الوسيط الثاني دالةً (عادةً ما تكون مجهولة) تعمل متى اكتمل الإجراء.
</p>

<p>
	وإليك مثالًا حقيقيًا قابل للتشغيل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_19" style="">
<span class="kwd">function</span><span class="pln"> loadScript</span><span class="pun">(</span><span class="pln">src</span><span class="pun">,</span><span class="pln"> callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let script </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'script'</span><span class="pun">);</span><span class="pln">
  script</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> src</span><span class="pun">;</span><span class="pln">
  script</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> callback</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">head</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">*!*</span><span class="pln">
loadScript</span><span class="pun">(</span><span class="str">'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js'</span><span class="pun">,</span><span class="pln"> script </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Cool</span><span class="pun">,</span><span class="pln"> the script $</span><span class="pun">{</span><span class="pln">script</span><span class="pun">.</span><span class="pln">src</span><span class="pun">}</span><span class="pln"> is loaded</span><span class="pun">`);</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln"> _ </span><span class="pun">);</span><span class="pln"> </span><span class="com">// التابع معرف في السكربت المُحمّل</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
</span><span class="pun">*/!*</span></pre>

<p>
	يُسمّى هذا الأسلوب في البرمجة غير المتزامنة ”الأسلوب المبني على ردود النداء“ (callback-based)، إذ على الدوال التي تنفّذ أمور غير متزامنة تقديمَ وسيط ردّ نداء <code>callback</code> نضع فيه الدالة التي ستُشغّل متى اكتملت تلك الأمور.
</p>

<p>
	ولقد فعلناها في <code>loadScript</code> ولكن بكلّ الأحوال هذا نهج عام.
</p>

<h2>
	رد النداء داخل رد النداء
</h2>

<p>
	وكيف نُحمّل سكربتين واحدًا تلو الآخر: يعمل الأول وبعدها حين ينتهي يعمل الثاني؟
</p>

<p>
	الحلّ الطبيعي الذي سيفكّر به الجميع هو وضع استدعاء <code>loadScript</code> الثاني داخل ردّ النداء، هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_21" style="">
<span class="pln">loadScript</span><span class="pun">(</span><span class="str">'/my/script.js'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="com">// جميل، اكتمل تحميل كذا، هيًا نُحمّل الآخر</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Cool</span><span class="pun">,</span><span class="pln"> the $</span><span class="pun">{</span><span class="pln">script</span><span class="pun">.</span><span class="pln">src</span><span class="pun">}</span><span class="pln"> is loaded</span><span class="pun">,</span><span class="pln"> let</span><span class="str">'</span><span class="pln">s load one more</span><span class="pun">`);</span><span class="pln">

  loadScript</span><span class="pun">(</span><span class="str">'/my/script2.js'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// جميل، اكتمل تحميل السكربت الثاني أيضًا</span><span class="pln">
    alert</span><span class="pun">(`</span><span class="typ">Cool</span><span class="pun">,</span><span class="pln"> the second script is loaded</span><span class="pun">`);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

</span><span class="pun">});</span></pre>

<p>
	وبعدما تكتمل الدالة الخارجية <code>loadScript</code>، يُشغّل ردّ النداء الدالة الداخلية.
</p>

<p>
	ولكن ماذا لو أردنا سكربتًا آخر، أيضًا…؟
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_23" style="">
<span class="pln">loadScript</span><span class="pun">(</span><span class="str">'/my/script.js'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  loadScript</span><span class="pun">(</span><span class="str">'/my/script2.js'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    loadScript</span><span class="pun">(</span><span class="str">'/my/script3.js'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// ...نواصل متى اكتملت كلّ السكربتات</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

  </span><span class="pun">})</span><span class="pln">

</span><span class="pun">});</span></pre>

<p>
	هكذا نضع كلّ إجراء جديد داخل ردّ نداء. لو كانت الإجراءات قليلة فليست مشكلة، ولكن لو زادت فستصير كارثية. سنرى ذلك قريبًا.
</p>

<h2>
	التعامل مع الأخطاء
</h2>

<p>
	لم نضع الأخطاء في الحسبان في هذه الأمثلة. ماذا لو فشل تحميل السكربت؟ يفترض أن تتفاعل دالة ردّ النداء بناءً على الخطأ.
</p>

<p>
	إليك نسخة محسّنة من الدالة <code>loadScript</code> تتعقّب أخطاء التحميل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_25" style="">
<span class="kwd">function</span><span class="pln"> loadScript</span><span class="pun">(</span><span class="pln">src</span><span class="pun">,</span><span class="pln"> callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let script </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'script'</span><span class="pun">);</span><span class="pln">
  script</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> src</span><span class="pun">;</span><span class="pln">

  </span><span class="com">// هنا</span><span class="pln">
  script</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> callback</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> script</span><span class="pun">);</span><span class="pln">
  </span><span class="com">// خطأ في تحميل السكربت كذا</span><span class="pln">
  script</span><span class="pun">.</span><span class="pln">onerror </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> callback</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(`</span><span class="typ">Script</span><span class="pln"> load error </span><span class="kwd">for</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">src</span><span class="pun">}`));</span><span class="pln">

  document</span><span class="pun">.</span><span class="pln">head</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هنا نستدعي <code>callback(null, script)‎</code> لو اكتمل التحميل، ونستدعي <code>callback(error)‎</code> لو لم يكتمل.
</p>

<p>
	طريقة الاستعمال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_27" style="">
<span class="pln">loadScript</span><span class="pun">(</span><span class="str">'/my/script.js'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// نتعامل مع الخطأ</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// تحمّل السكربت بنجاح</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نُعيد، ما استعملناه هنا للدالة <code>loadScript</code> هي طريقة مشهورة جدًا، تُدعى بأسلوب ”ردّ نداء الأخطاء أولًا“.
</p>

<p>
	إليك ما اصطُلح عليه:
</p>

<ol>
<li>
		يُحجز الوسيط الأوّل من دالة <code>callback</code> للخطأ إن حدث، ويُستدعى <code>callback(err)‎</code>.
	</li>
	<li>
		يكون الوسيط الثاني (وغيرها إن دعت الحاجة) للنتيجة الصحيحة،
	</li>
</ol>
<p>
	هكذا نستعمل الدالة <code>callback</code> فقط للأمرين: الإبلاغ عن الأخطاء وتمرير النتيجة.
</p>

<h2>
	هرم العذابات (Pyramid of Doom)
</h2>

<p>
	يمكن أن نرى أنّ البرمجة بنمط اللاتزامن مفيد جدًا، وهذا جليّ من النظرة الأولى. فحين نكون أمام استدعاء أو استدعاءين فقط، فأمورنا ممتازة.
</p>

<p>
	ولكن لو تتابعت الإجراءات غير المتزامنة واحدة تلو الأخرى، سنرى هذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_29" style="">
<span class="pln">loadScript</span><span class="pun">(</span><span class="str">'1.js'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    handleError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
    loadScript</span><span class="pun">(</span><span class="str">'2.js'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        handleError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// ...</span><span class="pln">
        loadScript</span><span class="pun">(</span><span class="str">'3.js'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            handleError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">);</span><span class="pln">
          </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// ‫...نُواصل بعد اكتمال تحميل كل السكربتات (*)</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">

      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	إليك ما في الشيفرة أعلاه:
</p>

<ol>
<li>
		نُحمّل <code>‎1.js</code> ونرى لو لم تحدث أخطاء.
	</li>
	<li>
		نُحمّل <code>‎2.js</code> ونرى لو لم تحدث أخطاء.
	</li>
	<li>
		نُحمّل <code>‎3.js</code>، ولو لم تحدث أخطاء نفّذنا شيئًا <code>(*)</code>.
	</li>
</ol>
<p>
	فكلّما تداخل الاستدعاءات أكثر أصبحت الشيفرة متشعّبة جدًا وأصعب في الإدارة كثيرًا، هذا خصوصًا لو كانت هناك شيفرة فعلية لا <code>...</code> (مثل الحلقات والعبارات الشرطية وغيرها).
</p>

<p>
	يُسمّون هذا أحيانًا ”بجحيم ردود النداء“ (callback hell) أو ”هرم العذابات“.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="47710" href="https://academy.hsoub.com/uploads/monthly_2020_06/callback-hell.png.11fc5118ff32f87d3104e3a16484092b.png" rel=""><img alt="callback-hell.png" class="ipsImage ipsImage_thumbnailed" data-fileid="47710" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_06/callback-hell.thumb.png.2e4879122eb969683345fe4e7444651a.png"></a>
</p>

<p>
	بزيادة الإجراءات غير المتزامنة واحدًا بعد آخر، يتشعّب ”هرم“ الاستدعاءات المتداخلة إلى اليمين أكثر فأكثر، ولن يمضي من الوقت الكثير حتى دخلتْ في دوّامة حلزونية محال تنظيمها.
</p>

<p>
	بهذا تُعدّ طريقة البرمجة هذه سيّئة.
</p>

<p>
	يمكننا في محاولة يائسة لتقليل وقع المشكلة تحويل كلّ إجراء إلى دالة منفردة، هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_31" style="">
<span class="pln">loadScript</span><span class="pun">(</span><span class="str">'1.js'</span><span class="pun">,</span><span class="pln"> step1</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> step1</span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    handleError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
    loadScript</span><span class="pun">(</span><span class="str">'2.js'</span><span class="pun">,</span><span class="pln"> step2</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> step2</span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    handleError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
    loadScript</span><span class="pun">(</span><span class="str">'3.js'</span><span class="pun">,</span><span class="pln"> step3</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> step3</span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> script</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    handleError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ‫...نواصل بعد اكتمال تحميل كل السكربتات (*)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	رأيت الفكرة؟ مبدأ الشيفرة واحد وليس هناك تداخلات متشعّبة إذ حوّلنا كلّ إجراء إلى دالة
</p>

<p>
	لا مشكلة في الشيفرة إذ هي تعمل، ولكنّها فعليًا مقطّعة إربًا إربًا، ويصعُب على الشخص قراءتها وغالبًا تنتقل بنظرك بين أجزاء الشيفرة وأنت تقرأها. ليس هذا بالأمر الجيد خصوصًا لو لم يكن قارئ الشيفرة يعلم بما تفعله أو إلى أين ينقل نظره.
</p>

<p>
	كما وأنّ الدوال بأسماء الخطوات <code>step*</code> هي لاستعمال واحد ولم نصنعها إلّا لتفادي ”هرم العذابات“. لن يأتي أحد لاحقًا ويُعيد استعمالها في أيّ شيء عدا سلسلة الإجراءات هذه. بهذا ”نلوّث“ فضاء الأسماء، إن جاز التعبير.
</p>

<p>
	نريد بالطبع ما هو أفضل من هذا الحل.
</p>

<p>
	ولحسن حظنا (كالعادة)، فهناك طرق أخرى نتجنّب بها الأهرام هذه، أفضلها هي استعمال ”الوعود“ وسنشرحها في الفصل القادم.
</p>

<h2>
	تمارين
</h2>

<h3>
	دائرة تتحرك ولها رد نداء
</h3>

<p>
	نرى في التمرين <a href="https://javascript.info/task/animate-circle" rel="external nofollow">Animated circle</a> دائرة يكبُر مقاسها في حركة جميلة.
</p>

<p>
	لنقل بأنّا لا نريد الدائرة فقط، بل أيضًا عرض رسالة فيها. يجب أن تظهر الرسالة <em>بعدما</em> ينتهي التحريك (أي تكون الدائرة بمقاسها الكبير الكامل)، وإلّا بدا النص قبيح المظهر.
</p>

<p>
	في حلّ ذاك التمرين، نرى الدالة <code>showCircle(cx, cy, radius)‎</code> ترسم الدائرة ولكن لا تُقدّم لنا أيّ طريقة نعرف بها انتهاء الرسم.
</p>

<p>
	أضِف وسيط ردّ نداء <code>showCircle(cx, cy, radius, callback)‎</code> يُستدعى متى اكتمل التحريك. على المُعامل <code>callback</code> استلام كائن <code>&lt;div&gt;</code> للدائرة وسيطًا له.
</p>

<p>
	إليك مثالًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3410_33" style="">
<span class="pln">showCircle</span><span class="pun">(</span><span class="lit">150</span><span class="pun">,</span><span class="pln"> </span><span class="lit">150</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> div </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  div</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">'message-ball'</span><span class="pun">);</span><span class="pln">
  div</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="str">"Hello, world!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تجربة حية:
</p>

<p>
	<iframe class="code-result__iframe" data-trusted="1" src="https://en.js.cx/task/animate-circle-callback/solution/" style="height:260px"></iframe>
</p>

<p>
	يمكنك اعتماد حل <a href="https://javascript.info/task/animate-circle" rel="external nofollow">هذا التمرين</a> منطلقًا للحل.
</p>

<h4>
	الحل
</h4>

<p>
	يمكنك معاينة الحل من خلال <a href="https://plnkr.co/edit/CwXJViltnzVEWbk2?p=preview" rel="external nofollow">المثال الحي</a>.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/callbacks" rel="external nofollow">Introduction: callbacks</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">914</guid><pubDate>Fri, 19 Jun 2020 06:58:52 +0000</pubDate></item><item><title>&#x627;&#x644;&#x623;&#x62E;&#x637;&#x627;&#x621; &#x627;&#x644;&#x645;&#x62E;&#x635;&#x635;&#x629; &#x648;&#x62A;&#x648;&#x633;&#x639;&#x629; &#x635;&#x646;&#x641; Error &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D9%85%D8%AE%D8%B5%D8%B5%D8%A9-%D9%88%D8%AA%D9%88%D8%B3%D8%B9%D8%A9-%D8%B5%D9%86%D9%81-error-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r909/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_06/69.jpg.86a296c5f1e2480150f6672fb6ef5a2f.jpg" /></p>

<p>
	متى نكون نطوّر البرامج نحتاج إلى أصناف أخطاء خاصة بنا لتوضّح تمامًا ما قد يحدث خطأً في المهام التي نقوم بها. فمثلًا لأخطاء الشبكة نستعمل <code>HttpError</code>، ولعمليات قواعد البيانات <code>DbError</code> ولعمليات البحث <code>NotFoundError</code> وهكذا.
</p>

<p>
	وعلى هذه الأخطاء أن تدعم الخاصيات الأساسية مثل الرسالة <code>message</code> والاسم <code>name</code> والمَكدس <code>stack</code> (يفضّل ذلك)، ولكن يمكن أن تحتوي على خصائص أخرى خاصة بها مثل خاصية <code>statusCode</code> لكائنات <code>HttpError</code> وتحمل قيمة من قيم رموز الحالة <code>404</code> أو <code>403</code> أو <code>500</code>.
</p>

<p>
	تتيح لنا جافاسكربت استعمال <code>throw</code> بتمرير أيّ وسيط، لذا فأصناف الخطأ الخاصة بنا يمكن ألّا ترث (تقنيًا) من كائن الخطأ <code>Error</code>، ولكن لو ورثنا منها فيمكنن للجميع استعمال <code>obj instanceof Error</code> لاحقًا لتتعرّف على كائنات الخطأ، بذلك يكون أفضل لو ورثناها.
</p>

<p>
	وكلّما كبر التطبيق شكّلت الأخطاء التي خصّصناها شجرة، فمثلًا سيرث الصنف <code>HttpTimeoutError</code> الصنفَ <code>HttpError</code>، وهكذا دواليك.
</p>

<h2>
	توسيع Error
</h2>

<p>
	لنأخذ مثالًا دالةَ <code>readUser(json)‎</code> تقرأ كائن JSON في بيانات المستخدم.
</p>

<p>
	وهذا مثال عن كائن <code>json</code> صالح:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2470_9" style="">
<span class="pln">let json </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`{</span><span class="pln"> </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"age"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">30</span><span class="pln"> </span><span class="pun">}`;</span></pre>

<p>
	سنستعمل في الشيفرة التابِع <code>JSON.parse</code>، وإن استلم كائن <code>json</code> معطوب رمى خطأ <code>SyntaxError</code>. ولكن، حتّى لو كان الكائن صحيحًا صياغيًا، فلا يعني هذا أنّ المستخدم صالحًا أيضًا، أم لا؟ لربّما لا يحتوي بعض البيانات مثل خاصيتي الاسم <code>json</code> والعمر <code>name</code> الضروريتين للمستخدمين.
</p>

<p>
	بهذا لن تقرأ الدالة <code>readUser(json)‎</code> كائن JSON فحسب، بل ستفحص (”تتحقّق من“) البيانات، فلو لم تكن الحقول المطلوبة موجودة، أو كان تنسيق الكائن مغلوطًا، فهنا نكون أمام خطأ… وهذا الخطأ ليس خطأً صياغيًا <code>SyntaxError</code> إذ أنّ البيانات صحيحة صياغيًا، بل نوع آخر من الأخطاء. سنسمّي هذا النوع <code>ValidationError</code> ونصنع صنف له. على هذا النوع من الأخطاء احتواء ما يلزم من معلومات تخصّ الحقل المخالف.
</p>

<p>
	يفترض علينا وراثة الصنف المضمّن في اللغة <code>Error</code> لصنفنا <code>ValidationError</code>. إليك شيء عن شيفرة الصنف المضمّن لنعرف ما نحاول توسعته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2470_11" style="">
<span class="com">// شيفرة مبسّطة لصنف الخطأ ‫Error المضمّن في لغة جافاسكربت نفسها</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Error</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">message </span><span class="pun">=</span><span class="pln"> message</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Error"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// (تختلف الأسماء باختلاف أصناف الأخطاء المضمّنة)</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">stack </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">call stack</span><span class="pun">&gt;;</span><span class="pln"> </span><span class="com">// ليست قياسية، إلّا أنّ أغلب البيئات تدعمها</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الآن صار وقت أن يرث الصنف <code>ValidationError</code> منها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2470_13" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">ValidationError</span><span class="pln"> extends </span><span class="typ">Error</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (1)</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"ValidationError"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ في التحقّق (2)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> test</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ValidationError</span><span class="pun">(</span><span class="str">"Whoops!"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// آخ</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  test</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">err</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln"> </span><span class="com">// آخ!</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">err</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="com">// خطأ في التحقّق</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">err</span><span class="pun">.</span><span class="pln">stack</span><span class="pun">);</span><span class="pln"> </span><span class="com">// قائمة من الاستدعاءات المتداخلة في كلّ منها رقم السطر</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ كيف أنّنا في السطر <code>(1)</code> استدعينا الباني الأب، إذ تطلب منا لغة جافاسكربت استدعاء <code>super</code> في البانيات الابنة، أي أنّه أمر إلزامي. يضبط الباني الأب خاصية الرسالة <code>message</code>.
</p>

<p>
	كما يضبط أيضًا خاصية الاسم <code>name</code> لتكون <code>"Error"</code>، ولذلك نُعدّلها إلى القيمة الصحيحة في السطر <code>(2)</code>.
</p>

<p>
	فلنحاول الآن استعماله في <code>readUser(json)‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2470_15" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">ValidationError</span><span class="pln"> extends </span><span class="typ">Error</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"ValidationError"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ في التحقّق</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// الاستعمال</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> readUser</span><span class="pun">(</span><span class="pln">json</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let user </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">json</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">user</span><span class="pun">.</span><span class="pln">age</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ValidationError</span><span class="pun">(</span><span class="str">"No field: age"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// حقل غير موجود: العمر</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ValidationError</span><span class="pun">(</span><span class="str">"No field: name"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// حقل غير موجود: الاسم</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> user</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// هنا نجرّب باستعمال try..catch</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let user </span><span class="pun">=</span><span class="pln"> readUser</span><span class="pun">(</span><span class="str">'{ "age": 25 }'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err instanceof </span><span class="typ">ValidationError</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="str">"Invalid data: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> err</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln"> </span><span class="com">// البيانات غير صالحة: حقل غير موجود: الاسم</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err instanceof </span><span class="typ">SyntaxError</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">
    alert</span><span class="pun">(</span><span class="str">"JSON Syntax Error: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> err</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫خطأ صياغي لكائن JSON</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> err</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ لا نعرفه، علينا إعادة رميه (**)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تتعامل كتلة <code>try..catch</code> في الشيفرة أعلاه النوعين من الأخطاء: <code>ValidationError</code> والخطأ المضمّن <code>SyntaxError</code> الذي يرميه التابِع <code>JSON.parse</code>.
</p>

<p>
	لاحِظ أيضًا كيف استعملنا <code>instanceof</code> لفحص نوع الخطأ في السطر <code>(*)</code>.
</p>

<p>
	يمكننا أيضًا مطالعة <code>err.name</code> هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2470_17" style="">
<span class="com">// ...</span><span class="pln">
</span><span class="com">// بدل ‫(err instanceof SyntaxError)</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">.</span><span class="pln">name </span><span class="pun">==</span><span class="pln"> </span><span class="str">"SyntaxError"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">
</span><span class="com">// ...</span></pre>

<p>
	ولكنّ استعمال <code>instanceof</code> أفضل بكثير إذ يحدث ونوسّع مستقبلًا الصنف <code>ValidationError</code> بأصناف فرعية منه مثل <code>PropertyRequiredError</code>، والفحص عبر <code>instanceof</code> سيظلّ يعمل للأصناف الموروثة منه،
</p>

<p>
	كما من المهمّ أن تُعيد كتلة <code>catch</code> رمي الأخطاء التي لا تفهمها، كما في السطر <code>(**)</code>. ليس على هذه الكتلة إلّا التعامل مع أخطاء التحقّق والصياغة، أمّا باقي الأخطاء (والتي قد تحدث بسبب الأخطاء المطبعية في الشيفرة أو غيرها من أمور غريبة عجيبة) فيجب أن تسقط أرضًا.
</p>

<h2>
	تعميق الوراثة
</h2>

<p>
	صنف الخطأ <code>ValidationError</code> عامٌ جدًا جدًا، إذ يمكن أن تحصل أمور كثيرة خطأً في خطأ. لربّما كانت الخاصية غير موجودة أو كان نسقها خطأ (مثل تقديم سلسلة نصية قيمةً للعمر <code>age</code>). لنصنع الصنف …. <code>PropertyRequiredError</code> ونستعمله فقط للخاصيات غير الموجودة، وسيحتوي على أيّة معلومات إضافية عن الخاصية الناقصة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2470_20" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">ValidationError</span><span class="pln"> extends </span><span class="typ">Error</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"ValidationError"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ في التحقّق</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// هذا</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">PropertyRequiredError</span><span class="pln"> extends </span><span class="typ">ValidationError</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">property</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(</span><span class="str">"No property: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> property</span><span class="pun">);</span><span class="pln"> </span><span class="com">// خاصية غير موجودة</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"PropertyRequiredError"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ إذ الخاصية مطلوبة</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">property </span><span class="pun">=</span><span class="pln"> property</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// الاستعمال</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> readUser</span><span class="pun">(</span><span class="pln">json</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let user </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">json</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">user</span><span class="pun">.</span><span class="pln">age</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">PropertyRequiredError</span><span class="pun">(</span><span class="str">"age"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// العمر</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">PropertyRequiredError</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// الاسم</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> user</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// هنا نجرّب باستعمال ‫try..catch</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let user </span><span class="pun">=</span><span class="pln"> readUser</span><span class="pun">(</span><span class="str">'{ "age": 25 }'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err instanceof </span><span class="typ">ValidationError</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="str">"Invalid data: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> err</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln"> </span><span class="com">// البيانات غير صالحة: خاصية غير موجودة: الاسم</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">err</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="com">// PropertyRequiredError</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">err</span><span class="pun">.</span><span class="pln">property</span><span class="pun">);</span><span class="pln"> </span><span class="com">// name</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err instanceof </span><span class="typ">SyntaxError</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="str">"JSON Syntax Error: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> err</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln"> </span><span class="com">// خطأ صياغي لكائن ‫JSON</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> err</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ لا نعرفه، علينا إعادة رميه</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يسهُل علينا استعمال الصنف الجديد <code>PropertyRequiredError</code>، فكلّ ما علينا تمريره هو اسم الخاصية: <code>new PropertyRequiredError(property)‎</code>، وسيُولّد الباني الرسالةَ <code>message</code> التي نفهمها نحن البشر.
</p>

<p>
	لاحظ كيف أنّنا أسندنا الخاصية <code>this.name</code> في باني <code>PropertyRequiredError</code> يدويًا، مرّة ثانية. قد ترى هذا الأمر مُتعبًا حيث ستُسند قيمة <code>this.name = &lt;class name&gt;‎</code> في كلّ صنف خطأ تصنعه. يمكن تجنّب هذا العناء وإسناد قيمة مناسبة لصنف ”الخطأ الأساس“ <code>this.name = this.constructor.name</code>، وبعدها نرث من هذا الصنف كلّ أصناف الأخطاء المخصّصة.
</p>

<p>
	لنسمّه مثلًا <code>MyError</code>. إليك شيفرة <code>MyError</code> وغيرها من أصناف أخطاء مخصّصة، ولكن مبسّطة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2470_22" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">MyError</span><span class="pln"> extends </span><span class="typ">Error</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">constructor</span><span class="pun">.</span><span class="pln">name</span><span class="pun">;</span><span class="pln"> </span><span class="com">// هنا</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ValidationError</span><span class="pln"> extends </span><span class="typ">MyError</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">PropertyRequiredError</span><span class="pln"> extends </span><span class="typ">ValidationError</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">property</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(</span><span class="str">"No property: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> property</span><span class="pun">);</span><span class="pln"> </span><span class="com">// خاصية غير موجودة</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">property </span><span class="pun">=</span><span class="pln"> property</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// الاسم صحيح</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">PropertyRequiredError</span><span class="pun">(</span><span class="str">"field"</span><span class="pun">).</span><span class="pln">name </span><span class="pun">);</span><span class="pln"> </span><span class="com">// PropertyRequiredError</span></pre>

<p>
	الآن صارت شيفرات الأخطاء المخصّصة أقصر بكثير (خاصّة <code>ValidationError</code>) إذ حذفنا السطر <code>"this.name = ...‎"</code> في الباني.
</p>

<h2>
	تغليف الاستثناءات
</h2>

<p>
	هدف الدالة <code>readUser</code> في الشيفرة أعلاه هو ”قراءة بيانات المستخدم“، ويمكن أن تحدث مختلف الأخطاء أثناء تنفيذ ذلك. حاليًا نرى <code>SyntaxError</code> و<code>ValidationError</code> فقط، ولكن في المستقبل العاجل ستصير الدالة <code>readUser</code> أكبر وأكبر وقد تُولّد لنا أنواع أخرى من الأخطاء.
</p>

<p>
	ومَن يتعامل مع هذه الأخطاء؟ الشيفرة التي تستدعي <code>readUser</code>! حاليًا لا تستعمل إلا بضعة تعابير شرطية <code>if</code> في كُتل <code>catch</code> (تفحص الصنف وتتعامل مع الأخطاء وتُعيد رمي ما لا تفهم)، وسيكون المخطط هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2470_24" style="">
<span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  readUser</span><span class="pun">()</span><span class="pln">  </span><span class="com">// الخطأ الأساسي هنا</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err instanceof </span><span class="typ">ValidationError</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ‫معالحة أخطاء ValidationError</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err instanceof </span><span class="typ">SyntaxError</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ‫معالجة الأخطاء الصياغية SyntaxError</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> err</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ مجهول فلنُعد رميه من جديد</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نرى في الشيفرة البرمجية أعلاه نوعين من الأخطاء ولكن ممكن أن يكون أكثر من ذلك.
</p>

<p>
	ولكن متى ولّدت الدالة <code>readUser</code> أنواع أخرى من الأخطاء، فعلينا طرح السؤال: هل علينا حقًا فحص كلّ نوع من أنواع الأخطاء واحدًا واحدًا في كلّ شيفرة تستدعي <code>readUser</code>؟
</p>

<p>
	عادةً ما يكون الجواب هو ”لا“، فالشيفرة الخارجية تفضّل أن تكون ”على مستوًى أعلى من ذلك المستوى“، أي أن تستلم ما يشبه ”خطأ في قراءة البيانات“، أمّا عن سبب حدوثه فلا علاقة لها به (طالما رسالة الخطأ تصف نفسها). أو ربّما (وهذا أفضل) تكون هناك طريقة لتحصل فيها على بيانات الخطأ، لو أرادت الشيفرة ذلك.
</p>

<p>
	تدعى الطريقة التي وصفناها بتغليف الاستثناءات.
</p>

<ol>
<li>
		<p>
			لنصنع الآن صنف ”خطأ في القراءة“ <code>ReadError</code> لنمثّل هذه الأخطاء.
		</p>
	</li>
	<li>
		<p>
			متى حدثت أخطاء داخل الدالة <code>readUser</code>، سنلتقطها فيها ونُولّد خطأ <code>ReadError</code>، بدلًا من <code>ValidationError</code> و <code>SyntaxError</code>.
		</p>
	</li>
	<li>
		<p>
			الكائن <code>ReadError</code> سيُبقي أيضًا إشارة إلى الخطأ الأصلي في خاصية السبب <code>cause</code>.
		</p>

		<p>
			لذا و هكذا لن يكون للشيفرة الخارجية (التي ستستدعي <code>readUser</code>) إلّا فحص <code>ReadError</code>. وليس كلّ نوع من أخطاء قراءة البيانات. وإن كان يحتاج لمزيد من التفاصيل عن الخطأ يمكنه العودة إلى الخاصية <code>cause</code> والتحقق منها.
		</p>
	</li>
</ol>
<p>
	إليك الشيفرة التي نُعرّف فيها الخطأ <code>ReadError</code> ونمثّل طريقة استعماله في الدالة <code>readUser</code> وفي <code>try..catch</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2470_26" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">ReadError</span><span class="pln"> extends </span><span class="typ">Error</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">message</span><span class="pun">,</span><span class="pln"> cause</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">cause </span><span class="pun">=</span><span class="pln"> cause</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'ReadError'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ في القراءة</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ValidationError</span><span class="pln"> extends </span><span class="typ">Error</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">/*...*/</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">PropertyRequiredError</span><span class="pln"> extends </span><span class="typ">ValidationError</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">/* ... */</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> validateUser</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">user</span><span class="pun">.</span><span class="pln">age</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">PropertyRequiredError</span><span class="pun">(</span><span class="str">"age"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// العمر</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">user</span><span class="pun">.</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">PropertyRequiredError</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// الاسم</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> readUser</span><span class="pun">(</span><span class="pln">json</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let user</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    user </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">json</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// هنا</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err instanceof </span><span class="typ">SyntaxError</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ReadError</span><span class="pun">(</span><span class="str">"Syntax Error"</span><span class="pun">,</span><span class="pln"> err</span><span class="pun">);</span><span class="pln"> </span><span class="com">// خطأ صياغي</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> err</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    validateUser</span><span class="pun">(</span><span class="pln">user</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// وهنا</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err instanceof </span><span class="typ">ValidationError</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ReadError</span><span class="pun">(</span><span class="str">"Validation Error"</span><span class="pun">,</span><span class="pln"> err</span><span class="pun">);</span><span class="pln"> </span><span class="com">// خطأ في التحقّق</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> err</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  readUser</span><span class="pun">(</span><span class="str">'{bad json}'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e instanceof </span><span class="typ">ReadError</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// هنا    alert(e);</span><span class="pln">
    </span><span class="com">// Original error: SyntaxError: Unexpected token b in JSON at position 1</span><span class="pln">
    alert</span><span class="pun">(</span><span class="str">"Original error: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> e</span><span class="pun">.</span><span class="pln">cause</span><span class="pun">);</span><span class="pln"> </span><span class="com">// الخطأ الأصل</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> e</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في الشيفرة أعلاه تعمل الدالة <code>readUser</code> تمامًا كم وصفها، تلتقط أخطاء الصياغة والتحقّق وترمي أخطاء قراءة <code>ReadError</code> بدلها (كما وتُعيد رمي الأخطاء المجهولة أيضًا).
</p>

<p>
	هكذا ليس على الشيفرة الخارجية إلّا فحص <code>instanceof ReadError</code> فقط، لا داعٍ للتحقّق من كلّ خطأ يمكن أن يحصل في هذه المجرّة.
</p>

<p>
	يُسمّى هذا النهج ”بتغليف الاستثناءات“ حيث نستلم نحن ”الاستثناءات في المستوى المنخفض من البرمجة“ (low level) و”نُغلّفها“ داخل خطأ <code>ReadError</code> يكون أكثر بساطة وأسهل استعمالًا للشيفرات التي تنادي على الدوال. هذا النهج مستعمل بكثرة في البرمجة كائنية التوجّه.
</p>

<h2>
	خلاصة
</h2>

<ul>
<li>
		يمكننا وراثة صنف الخطأ <code>Error</code> وغيرها من أخطاء مضمّنة كما الوراثة العادية. المهم أن ننتبه من خاصية الاسم <code>name</code> ولا ننسى استدعاء <code>super</code>.
	</li>
	<li>
		يمكننا استعمال <code>instanceof</code> لفحص ما نريد من أخطاء بدقّة، كما ويعمل المُعامل مع الوراثة. أحيانًا نستلم كائن خطأ من مكتبة طرف ثالث وما من طريقة سهلة لنعرف اسم صنفها. هنا يمكن استعمال خاصية الاسم <code>name</code> لإجراء هذا الفحص.
	</li>
	<li>
		أسلوب تغليف الاستثناءات هو أسلوب منتشر الاستعمال، فيه تتعامل الدالة مع الاستثناءات في المستوى المنخفض من البرمجة، وتصنع أخطاء مستواها عالٍ بدل تلك المنخفضة. وأحيانًا تصير الاستثناءات المنخفضة خصائص لكائن الخطأ (تمامًا مثل <code>err.cause</code> في الأمثلة أعلاه)، ولكنّ هذا ليس إلزاميًا أبدًا.
	</li>
</ul>
<h2>
	تمارين
</h2>

<h3>
	الوراثة من SyntaxError
</h3>

<p>
	<em>الأهمية: 5</em>
</p>

<p>
	اصنع الصنف <code>FormatError</code> ليرث من الصنف المضمّن <code>SyntaxError</code>.
</p>

<p>
	يجب أن يدعم الصنف خصائص الاسم <code>name</code> والرسالة <code>message</code> والمَكدس <code>stack</code>.
</p>

<p>
	طريقة الاستعمال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2470_28" style="">
<span class="pln">let err </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">FormatError</span><span class="pun">(</span><span class="str">"formatting error"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// خطأ في التنسيق</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> err</span><span class="pun">.</span><span class="pln">message </span><span class="pun">);</span><span class="pln"> </span><span class="com">// خطأ في التنسيق</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> err</span><span class="pun">.</span><span class="pln">name </span><span class="pun">);</span><span class="pln"> </span><span class="com">// FormatError</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> err</span><span class="pun">.</span><span class="pln">stack </span><span class="pun">);</span><span class="pln"> </span><span class="com">// المَكدس</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> err instanceof </span><span class="typ">FormatError</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> err instanceof </span><span class="typ">SyntaxError</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// ‫true (إذ يرث الصنف الصنفَ SyntaxError)</span></pre>

<h4>
	الحل
</h4>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2470_30" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">FormatError</span><span class="pln"> extends </span><span class="typ">SyntaxError</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">constructor</span><span class="pun">.</span><span class="pln">name</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let err </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">FormatError</span><span class="pun">(</span><span class="str">"formatting error"</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> err</span><span class="pun">.</span><span class="pln">message </span><span class="pun">);</span><span class="pln"> </span><span class="com">// خطأ في التنسيق</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> err</span><span class="pun">.</span><span class="pln">name </span><span class="pun">);</span><span class="pln"> </span><span class="com">// خطأ في التنسيق</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> err</span><span class="pun">.</span><span class="pln">stack </span><span class="pun">);</span><span class="pln"> </span><span class="com">// stack(المَكدس)</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> err instanceof </span><span class="typ">SyntaxError</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span></pre>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/custom-errors" rel="external nofollow">Custom errors, extending Error</a> من كتاب <a href="https://javascript.info/js" rel="external nofollow">The JavaScript language</a>
</p>
]]></description><guid isPermaLink="false">909</guid><pubDate>Thu, 11 Jun 2020 05:30:11 +0000</pubDate></item></channel></rss>
