الفرق بين المراجعتين لصفحة: «JavaScript/Promise»
جميل-بيلوني (نقاش | مساهمات) إنشاء الصفحة. هذه الصفحة من مساهمات عبد اللطيف ايمش |
جميل-بيلوني (نقاش | مساهمات) طلا ملخص تعديل |
||
سطر 1: | سطر 1: | ||
<noinclude>{{DISPLAYTITLE:الكائن <code>Promise</code> في JavaScript | <noinclude>{{DISPLAYTITLE:الكائن <code>Promise</code> في JavaScript}}</noinclude> | ||
يُمثِّل الكائن <code>Promise</code> إكمال (أو فشل) عملية غير متزامنة (asynchronous operation)، والنتيجة المعادة منها. | يُمثِّل الكائن <code>Promise</code> إكمال (أو فشل) عملية غير متزامنة (asynchronous operation)، والنتيجة المعادة منها. | ||
مراجعة 07:49، 19 أبريل 2019
يُمثِّل الكائن Promise
إكمال (أو فشل) عملية غير متزامنة (asynchronous operation)، والنتيجة المعادة منها.
ملاحظة: هذه الصفحة تشرح الدالة البانية Promise
والدوال والخاصيات التابعة لتلك الكائنات. لتعلّم المزيد عن طريقة عمل الوعود (promises) وكيف يمكنك استخدامها، فننصحك بقراءة الصفحة «استخدام الوعود» أولًا. تُستخدَم الدالة البانية بشكل رئيسي لتغليف الدوال التي لا تدعم الوعود.
إليك مثال عن استخدام الدالة البانية للكائن Promise
لإنشاء وعد بسيط:
var promise1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 300);
});
promise1.then(function(value) {
console.log(value);
// "foo" الناتج المتوقع
});
console.log(promise1);
// [object Promise] الناتج المتوقع
البنية العامة
new Promise(executor /*function(resolve, reject) { ... } */);
المعاملات
executor
الدالة التي ستُمرَّر إلى الدالة البانية والتي لها وسيطين هما resolve
و reject
.
الدالة executor
ستُنفَّذ مباشرةً من آلية تنفيذ الوعود في المتصفح، مع تمرير الدوال resolve
و reject
إليها (ستُستدعى الدالة executor
مباشرةً قبل أن تعيد الدالة البانية Promise
كائنًا).
عند استدعاء الدالة resolve
و reject
ستقبل (resolve) أو ترفض (reject) الوعد على التوالي وبالترتيب.
ستضم الدالة executor
عادةً بعض العمليات غير المتزامنة، وبعد أن تنتهي من عملها فإما أن تستدعي الدالة resolve
لقبول الوعد، أو reject
إذا حدث خطأ.
إذا رُمي خطأٌ في الدالة executor
، فسيُرفضَ الوعد، وسيتم تجاهل القيمة المُعادة من الدالة executor
.
الوصف
الكائن Promise
هو كائنٌ وسيط (proxy) لقيمةٍ غير معروفة تحديدًا عند إنشاء الوعد، مما يسمح لنا بربط دوال لمعالجة الأحداث غير المتزامنة التي تكون نهايتها النجاح أو الفشل. وهذا يسمح للدوال غير المتزامنة (asynchronous) أن تعيد قيمًا مثلها كمثل الدوال المتزامنة؛ لكن بدلًا من إعادة القيمة النهائية مباشرةً، فإنَّ الدوال غير المتزامنة تعيد «وعدًا» بتوفير القيمة في مرحلة ما من المستقبل.
يكون الكائن Promise
في إحدى الحالات الآتية:
- pending (في الانتظار): الحالة المبدئية، أي لم يُحقَّق الوعد أو يُرفَض.
- fulfilled (محقق): يعني أنَّ العملية قد أُكمِلَت بنجاح.
- rejected (مرفوض): يعني أنَّ العملية فشلت.
الوعد الذي «في الانتظار» يمكن أن يكون «محققًا» ويعيد قيمةً، أو «مرفوضًا» مع سبب (خطأ). عند حدوث أحد الخيارين السابقين، فستُستدعى إحدى دوال المعالجة باستخدام الدالة then
التابعة للكائن Promise
.
إذا كان الوعد محققًا أو مرفوضًا عند ربط دالة المعالجة معه، فستستدعى دالة المعالجة، لذا لن تحدث «حالة سباق» (race condition) بين إكمال العملية غير المتزامنة، وبين دوال المعالجة التي يجري ربطها مع الوعد.
لمّا كانت الدالتان Promise.prototype.then()
و Promise.prototype.catch()
تعيدان بدورهما وعودًا، فيمكن استخدامهما في سلسلة.
[صورة]
ملاحظة: تملك بعض لغات البرمجة الأخرى آليات لتأخير تقييم قيمة تعبير أو تأجيل عملية حسابية، والتي تسمى عندها «بالوعود» (مثل Scheme). أما الوعود في JavaScript تُمثِّل العمليات التي تحدث فعلًا، والتي يمكن ربطها مع دوال رد النداء (callback functions). إذا كنتَ تبحث عن طريقة لتأخير تقييم قيمة تعبير، ففكر باستخدام الدوال السهمية دون وسائط كما في f = () => expression
لتأخير تقييم قيمة التعبير، واستخدام f()
لتقييم قيمته.
الخاصيات
Promise.length
قيمة الخاصية length
تساوي 1 دومًا، وهو عدد وسائط الدالة البانية.
Promise.prototype
تُمثِّل هذه الخاصية سلسلة prototype
للدالة البانية Promise
.
التوابع
تعيد وعدًا الذي يمكن أن يتحقق (fulfills) عندما تكون جميع الوعود في الوسيط iterable
محققةً، أو يُرفَض (rejects) عند حدوث أول رفض لأحد الوعود الموجودة في الوسيط iterable
.
إذا تحققت الوعود في هذه الدالة، فستحقق هذه الدالة مع إعادة مصفوفة فيها قيم الوعود المحققة في نفس ترتيب تعريفها في الوسيط iterable
. أما إذا رُفضِ أحد الوعود، فستُرفَض هذه الدالة مع سبب رفض أول وعد مُعرَّف في الوسيط iterable
. يمكن الاستفادة من هذه الدالة في تجميع نتائج عدّة وعود معًا.
تعيد وعدًا الذي إما أن يُحقَّق أو يُرفَض عند قبول أو رفض واحد من الوعود الموجودة في iterable
، مع إعادة القيمة أو السبب من ذاك الوعد.
تعيد كائن Promise
مرفوض، مع تحديد سبب (reason) الرفض.
تعيد كائن Promise
مقبول مع القيمة (value) المحددة. إذا ارتبطت هذه القيمة بالدالة then
، فسيتّبع (follow) الوعد المعاد تلك الدالة المرتبطة بالدالة then
، أو سيكون الوعد المُعاد محققًا مع القيمة value
.
إذا لم تكن تعرف إذا كانت القيمة value
وعدًا أم لا، فاستخدم الدالة Promise.resolve(value)
عليها، وتعامل مع القيمة المُعادة كوعد.
Promise prototype
الخاصيات
Promise.prototype.constructor
تعيد الدالة التي تُنشِئ نسخةً من الكائن Promise
، وهي الدالة البانية.
الدوال
Promise.prototype.catch(onRejected)
تضيف دالة رد نداء (callback) للتعامل مع حالة رفض (rejection) للوعد، وتعيد وعدًا جديدًا يُقبَل (resolve) إلى القيمة المعادة من دالة رد النداء إذا اُستدعيت، أو إلى قيمة الوعد المحقق إذا تحقق الوعد بدلًا من رفضه.
Promise.prototype.then(onFulfilled, onRejected)
تضيف دوالًا لمعالجة حالة التحقيق والرفض لوعدٍ ما، وتعيد وعدًا جديدًا يُقبَل إلى القيمة المعادة من دالة المعالجة، أو لإلى القيمة الأصلية للوعد إذا لم يُعالَج (أي أنَّ الوسيط onFulfilled
أو onRejected
لم يكن دالةً).
Promise.prototype.finally(onFinally)
تضيف دالة معالجة إلى الوعد، وتعيد وعدًا جديدًا يقبل (resolve) عندما يُقبَل الوعد الأصلي. وستُستدعى دالة المعالجة onFinally
عند انتهاء تنفيذ الوعد، سواءً تحقق أو رفض.
إنشاء وعد
يمكن إنشاء كائن Promise
عن طريق الكلمة المفتاحية new
والدالة البانية الخاصة به. هذه الدالة البانية تأخذ وسيطًا هو دالة تسمى «الدالة المنفذة» (executor function). ويجب أن تأخذ هذه الدالة دالتين كمعاملين، أولهما (resolve) ستستدعى عندما تكتمل المهمة غير المتزامنة بنجاح وتُعيد نتائج المهمة كقيمة، أما الدالة الثانية (reject) فستستدعى عند فشل المهمة، وتعيد سبب الفشل، ويكون عادةً كائن الخطأ.
const myFirstPromise = new Promise((resolve, reject) => {
// القيام بعملية غير متزامنة حتى استدعاء دالة:
//
// resolve(someValue); => القبول
// أو
// reject("failure reason"); => الرفض
});
لتوفير دالة لها إمكانيات الوعود، فاجعلها تعيد وعدًا:
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
}
أمثلة
مثال بسيط
سنستدعي الدالة resolve()
عند نجاح تنفيذ المهمة غير المتزامنة، والدالة reject()
عند فشل تنفيذ تلك المهمة.
سنستخدم في المثال الآتي الدالة setTimeout()
لمحاكاة شيفرة غير متزامنة. في الواقع، ستستخدم شيئًا يشبه تقنية Ajax (أي الكائن XHR) أو الواجهة البرمجية للغة HTML5.
ثم سنستخدم الدالة then()
لمعالجة القيمة المعادة من الوعد، وستكون قيمة المعامل successMessage
مساويةً للسلسلة النصية التي مررناها للدالة resolve()
:
let myFirstPromise = new Promise((resolve, reject) => {
setTimeout(function(){
resolve("Success!"); // كل شيءٍ جرى على ما يرام
}, 250);
});
myFirstPromise.then((successMessage) => {
console.log("Yay! " + successMessage);
});
مثال متقدم
هذا المثال الصغير يبيّن آلية عمل الكائن Promise
. ستُستدعى الدالة testPromise()
في كل مرة يُضغط فيها على العنصر <button>
، إذ سيُنشَأ وعد التي سيتحقق ويعيد عدد الوعود (العدّ يبدأ من 1) كل 1-3 ثانية (عشوائيًا) باستخدام الدالة setTimeout()
. تُستخدَم الدالة البانية Promise()
لإنشاء ذاك الوعد.
سنسجِّل تحقق الوعد في كل مرة، باستخدام الدالة then()
. وستكون هنالك سجلات تُظهِر كيف يمكن الدمج بين الشيفرة المتزامنة في الدالة مع إكمال الوعد في الشيفرة غير المتزامنة:
'use strict';
var promiseCount = 0;
function testPromise() {
let thisPromiseCount = ++promiseCount;
let log = document.getElementById('log');
log.insertAdjacentHTML('beforeend', thisPromiseCount +
') Started (<small>Sync code started</small>)<br/>');
// سنُنشِئ وعدًا جديدًا
let p1 = new Promise(
// هذه الدالة قادرة على تحقيق أو رفض الوعد
// reject the promise
(resolve, reject) => {
log.insertAdjacentHTML('beforeend', thisPromiseCount +
') Promise started (<small>Async code started</small>)<br/>');
// هذا مثال بسيط عن إنشاء عدم تزامن
window.setTimeout(
function() {
// حققنا الوعد
resolve(thisPromiseCount);
}, Math.random() * 2000 + 1000);
}
);
// then سنُعرِّف ماذا سيحدث عند قبول الوعد باستخدام
// catch وسنعرف ماذا سيحدث عند رفض الوعد باستخدام
p1.then(
// تسجيل القيمة
function(val) {
log.insertAdjacentHTML('beforeend', val +
') Promise fulfilled (<small>Async code terminated</small>)<br/>');
}).catch(
// تسجيل سبب الرفض
(reason) => {
console.log('Handle rejected promise ('+reason+') here.');
});
log.insertAdjacentHTML('beforeend', thisPromiseCount +
') Promise made (<small>Sync code terminated</small>)<br/>');
}