الدالة Promise.prototype.then()
في JavaScript
الدالة then()
تعيد وعدًا Promise
، وتأخذ وسيطين على الأكثر، وهما دالتا رد النداء (callback) لنجاح أو فشل الوعد.
var promise1 = new Promise(function(resolve, reject) {
resolve('Success!');
});
promise1.then(function(value) {
console.log(value);
// "Success!"
});
ملاحظة: إذا كان أحد أو كلا المعاملين غير موجود، أو كان موجودًا لكنه ليس دالةً، فلن تضع الدالة then()
معالجًا له، لكنها لن تولِّد أي أخطاء. إذا أصبحت هنالك حالة للوعد Promise
الذي استدعيت الدالة ()then
عليه، ولم يكن للدالة ()then
وسائط مناسبة، فسيُنشَأ وعدٌ Promise
جديد دون دوال للمعالجة، وذلك بأخذ آخر حالة للوعد Promise
الأصلي الذي اُستدعيت عليه الدالة ()then
.
البنية العامة
p.then(onFulfilled[, onRejected]);
p.then(function(value) {
// القبول
}, function(reason) {
// الرفض
});
المعاملات
onFulfilled
دالة ستُستدعى عند قبول الوعد Promise
. تملك هذه الدالة معاملًا وحيدًا هو قيمة القبول. إذا لم تكن هذه الدالة موجودًا، فستستبدل داخليًا بدالة تُعيد الوسيط المُمرَّر إليها.
onRejected
هذا المعامل اختياري. وهو دالة ستُستدعى عند رفض الوعد Promise
. تملك هذه الدالة معاملًا وحيدًا هو سبب الرفض. إذا لم تكن هذه الدالة موجودًا، فستستبدل داخليًا بدالة تعيد رمي الوسيط المُمرَّر إليها.
القيمة المعادة
كائن Promise
في حالة الانتظار (pending). ثم ستستدعى دالة المعالجة (onFilfilled
أو onRejected
) استدعاءً غير متزامن، وبعد استدعاء الدالة المعالجة، إذا كانت:
- تعيد قيمةً، فالوعد المعاد من
then
سيقبل مع تلك القيمة.
- ترمي خطأً، فالوعد المعاد من
then
سيُرفض وسيرمى خطأٌ مع تلك القيمة.
- تعيد وعدًا مقبولًا من قبل، وسيقبل الوعد المُعاد من
then
مع قيمة الوعد الأصلي.
- تعيد وعدًا مرفوضًا من قبل، وسيرفض الوعد المعاد من
then
مع قيمة الوعد الأصلي.
- تعيد كائن
Promise
آخر في حالة الانتظار، وسيكون قبول أو رفض الوعد المُعاد منthen
تابعًا لقبول أو رفض الوعد المعاد من دالة المعالجة. وستكون قيمة الوعد المعاد من الدالةthen
هي نفس قيمة الوعد المعُاد من دالة المعالجة.
هذا مثال يوضِّح الآلية غير المتزامنة للدالة ()then
. لاحظ أننا نستخدم وعدًا مقبولًا، لذا ستستدعى ()then
مباشرةً، لكن الدوال المعالجة فيها ستُنفَّذ بشكل غير متزامن كما هو واضح من الرسائل الظاهرة:
var resolvedProm = Promise.resolve(33);
var thenProm = resolvedProm.then(function(value){
console.log("this gets called after the end of the main stack. the value received and returned is: " + value);
return value;
});
console.log(thenProm);
// سنؤجل تنفيذ الدالة
setTimeout(function(){
console.log(thenProm);
});
// الناتج بالترتيب هو
// Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
// "this gets called after the end of the main stack. the value received and returned is: 33"
// Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 33}
الوصف
لمّا كانت الدالة ()then
والدالة Promise.prototype.catch()
تعيدان وعودًا (الكائن Promise
)، فيمكن تنفيذها على شكل سلسلة (chain).
أمثلة
استخدام الدالة ()then
var p1 = new Promise( (resolve, reject) => {
resolve('Success!');
// or
// reject ("Error!");
} );
p1.then( value => {
console.log(value); // Success!
}, reason => {
console.log(reason); // Error!
} );
استخدام الدالة كسلسلة
تُعيد الدالة ()then
كائنًا من النوع Promise
، مما يسمح باستدعاء الدوال كسلسلة (chain). إذا كانت الدالة المُمرَّرة كدالة معالجة إلى الدالة ()then
تُعيد كائن Promise
، فيمكن أن يكون الكائن Promise
المعاد يستطيع أن ينفِّذ الدالة ()then
.
المثال الآتي يحاكي شيفرة غير متزامنة مع الدالة setTimeout
:
- الخطوة الأولى هي الحصول على
foo
ودمجbar
إليه، وقبول الوعد واستدعاء()then
.
- الخطوة الثانية هي الحصول على
foobar
، وتسجيل دالة رد نداء (callback) التي تجري عملية على تلك السلسلة النصية، وتطبعها، لكن ذلك لن يحدث قبل إعادة السلسلة النصية الأصلية إلى الدالة()then
التالية.
- الخطوة الثالثة هي طباعة رسائل مفيدة.
لاحظ أنَّ السلسلة النصية الأخيرة string
لن تحتوي على baz
في آخرها، وذلك لأننا أضفنا ذلك بشكل غير متزامن داخل الدالة setTimeout
:
Promise.resolve('foo')
// الخطوة الأولى
.then(function(string) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
string += 'bar';
resolve(string);
}, 1);
});
})
// الخطوة الثانية
.then(function(string) {
setTimeout(function() {
string += 'baz';
console.log(string);
}, 1)
return string;
})
// الخطوة الثالثة
.then(function(string) {
console.log("Last Then: oops... didn't bother to instantiate and return " +
"a promise in the prior then so the sequence may be a bit " +
"surprising");
console.log(string);
});
عند إعادة القيمة ببساطة داخل دالة معالجة في ()then
، فستُعاد Promise.resolve
(<القيمة المعادة>):
var p2 = new Promise(function(resolve, reject) {
resolve(1);
});
p2.then(function(value) {
console.log(value); // 1
return value + 1;
}).then(function(value) {
console.log(value + '- This synchronous usage is virtually pointless'); // 2- This synchronous usage is virtually pointless
});
p2.then(function(value) {
console.log(value); // 1
});
استدعاء ()then
سيعيد وعدًا مرفوضًا إذا رمت الدالة استثناءً، أو أعادت وعدًا مرفوضًا:
Promise.resolve()
.then( () => {
// رمي استثناء
throw 'Oh no!';
})
.then( () => {
console.log( 'Not called.' );
}, reason => {
console.error( 'onRejected function called: ' + reason );
});
في جميع الحالات الأخرى، يعاد وعدًا مقبولًا. في المثال التالي، سيعيد الاستدعاء ()then الأول القيمة 42 مغلفة بوعد مقبول رغم رفض الوعد السابق في السلسلة نفسها:
Promise.reject()
.then( () => 99, () => 42 ) // القيمة 42 مغلفةً بوعد مقبول onRejected يعيد
.then( solution => console.log( 'Resolved with ' + solution ) ); // 42 يقبل الوعد مع القيمة
عمليًّا، يفضل التقاط الوعود المرفوضة بدلًا من استعمال حالة الصياغة الثنائية كما موضح في المثال التالي:
Promise.resolve()
.then( () => {
// يعيد وعدًا مرفوضًا .then() جعل
throw new Error('Oh no!');
})
.catch( error => {
console.error( 'onRejected function called: ' + error.message );
})
.then( () => {
console.log( "I am always called even if the prior then's promise rejects" );
});
يمكن ربط الاستدعاءات بسلسلة لتنفيذ دالة واحدة مع واجهة برمجية تعتمد على الوعود (Promise-based API) فوق دالة أخرى:
function fetch_current_data() {
// وعدًا. تعرض هذه الدالة fetch() تعيد الواجهة البرمجية
// واجهة برمجية مماثلة باستثناء أن القيمة المنجزة لوعد
// هذه الدالة لديه عملًا إضافيًّا لينجزه معها
return fetch('current-data.json').then((response) => {
if (response.headers.get('content-type') != 'application/json') {
throw new TypeError();
}
var j = response.json();
// j يمكن فعل شيء مع
return j; // تعطى القيمة المنجزة لمستخدم
// fetch_current_data().then()
});
}
I
إن أعاد onFulfilled وعدًا، فسيقبل أو يرفض هذا الوعد قيمة ()then
:
function resolveLater(resolve, reject) {
setTimeout(function () {
resolve(10);
}, 1000);
}
function rejectLater(resolve, reject) {
setTimeout(function () {
reject(new Error('Error'));
}, 1000);
}
var p1 = Promise.resolve('foo');
var p2 = p1.then(function() {
// إعادة وعد هنا والذي سيقبل مع القيمة 10 بعد 1 ثانية
return new Promise(resolveLater);
});
p2.then(function(v) {
console.log('resolved', v); // "resolved", 10
}, function(e) {
// لا تستدع
console.log('rejected', e);
});
var p3 = p1.then(function() {
// بعد 1 ثانية 'Error' إعادة وعد هنا والذي سيرفض مع خطأ
return new Promise(rejectLater);
});
p3.then(function(v) {
// لا تستدع
console.log('resolved', v);
}, function(e) {
console.log('rejected', e); // "rejected", 'Error'
});
نقص الدعم للنمط window.setImmediate
الذي يعتمد على الوعود
استعمال التابع Function.prototype.bind()
Reflect.apply
(Reflect.apply()
) لإنشاء دالة بنمط setImmediate
غير قابلة للإلغاء (non-cancellable):
const nextTick = (()=>{
const noop = () => {}; // حرفيًّا
const nextTickPromise = () => Promise.resolve().then(noop);
const rfab = Reflect.apply.bind; // (thisArg, fn, thisArg, [...args])
const nextTick = (fn, ...args) => (
fn !== undefined
? Promise.resolve(args).then(rfab(null, fn, null))
: nextTickPromise(),
undefined
);
nextTick.ntp = nextTickPromise;
return nextTick;
})();
S
انظر أيضًا
- الدالة
catch()
: تعيد وعدًاPromise
وتتعامل مع حالات رفض الوعود فقط. - الدالة
finally()
: تعيد وعدًاPormise
، وعندما تُقرَّر قيمة الوعد، سواءً كان مقبولًا أو مرفوضًا، فستُنفَّذ دالة رد النداء (callback) المُحدَّدة.