Promise.try() — يلمّ الخطأ المتزامن ويوحّد sync و async
Promise.try() بقى Baseline Newly من يناير ٢٠٢٥: لف أي callback (sync أو async) في Promise واحد، ويلمّ الـ throw المتزامن. Chrome/Edge 128، Firefox 134، Safari 18.2، Node 23 — من MDN.
لو عندك API بياخد callback، والـ callback ده ممكن يكون sync أو async، عايز تلف النتيجة في Promise واحد عشان تتعامل معاها بنفس الطريقة… حيلة Promise.resolve(func()) بتبوّظك لما func() ترمي خطأ متزامن. الحل اللي بقى Baseline: Promise.try().
المشكلة
Promise.resolve(func()) شغّال لو الـ function رجّعت قيمة أو Promise. لكن لو رمت throw بشكل متزامن، الخطأ بيطلع برّا السلسلة ومش بيتحوّل لـ rejected Promise. حسب MDN، الـ try...catch حوالين التعبير مش expression، فمش هتقدر تمرّره بسهولة لدوال تانية.
ناس كتير بتعمل:
new Promise((resolve) => resolve(func()));
ده بيلمّ الخطأ المتزامن، بس بيعمل Promise جديد حتى لو func() أصلًا رجّعت Promise جاهز. Promise.resolve() أذكى في النقطة دي، بس مش بيلمّ الـ throw.
الحل: Promise.try
Promise.try(func) بياخد callback من أي نوع — رجّع قيمة، رمى خطأ، أو رجّع Promise — وبيحوّل النتيجة لـ Promise:
- fulfilled لو رجّعت قيمة متزامنة
- rejected لو رميت خطأ متزامن
- fulfilled/rejected بشكل غير متزامن لو رجّعت Promise
function doSomething(action) {
return Promise.try(action)
.then((result) => console.log(result))
.catch((error) => console.error(error))
.finally(() => console.log("Done"));
}
doSomething(() => "Sync result");
doSomething(() => {
throw new Error("Sync error");
});
doSomething(async () => "Async result");
doSomething(async () => {
throw new Error("Async error");
});
نفس الفكرة بـ async/await هتبقى try/catch/finally جوه الدالة. Promise.try بيخلّي سلسلة الـ Promise تبان زي الـ sync error handling.
مش زي then
مهم: Promise.try(func) مش مكافئ لـ Promise.resolve().then(func). الـ callback في then بيتنادى دايمًا بشكل غير متزامن، بينما Promise.try بينادي الدالة بشكل متزامن، ولو قدرت يحل الـ Promise فورًا.
arguments زيادة
زي setTimeout، تقدر تمرّر arguments للـ callback من غير closure زيادة:
Promise.try(func, arg1, arg2);
// بدل
Promise.try(() => func(arg1, arg2));
التوافق (من MDN BCD)
MDN بيصنّفه Baseline 2025 — Newly available من يناير ٢٠٢٥ (لما Firefox وصل). حسب جداول التوافق:
- Chrome / Edge: من 128
- Firefox: من 134
- Safari / iOS: من 18.2
- Node.js: من 23.0.0
- Deno: من 1.46 · Bun: من 1.1.22
لو لسه على Node 22 أو متصفح أقدم، متبنيش عليه من غير polyfill أو شرط runtime.
مثال عملي: doSomething
تخيل عندك helper بياخد action من المستخدم أو من plugin. من غير Promise.try هتلف بـ try/catch يدوي أو هتخاطر بـ unhandled sync throw. معاه السلسلة واحدة:
Promise.try(action)
.then(handleOk)
.catch(handleErr)
.finally(cleanup);
ده مفيد في adapters بين مكتبات قديمة (callbacks متزامنة) و pipelines حديثة (async). كمان الـ method generic — تقدر تناديه على subclass من Promise لو الـ constructor بنفس توقيع الـ executor.
رأيي
أي مكان عندك callback مش عارف sync ولا async — handlers، plugins، adapters — Promise.try هو الـ default النضيف. أقل boilerplate من try/catch + Promise.resolve، وبيخلّي الـ error path واحد. بس اتأكد من الـ runtime قبل ما تعتمد عليه في production على Node LTS القديم.
المصادر
- JavaScript
- Promise
- FullStack
- NodeJS
- WebDev
- Baseline