جافاسكريبت - استخدام async await مع Array.map
بالنظر إلى الكود التالي:
var arr = [1,2,3,4,5];
var results: number[] = await arr.map(async (item): Promise<number> => {
await callAsynchronousOperation(item);
return item + 1;
});
الذي ينتج الخطأ التالي:
TS2322: النوع 'Promise
[]' غير قابل للتعيين إلى النوع 'number[]'. النوع 'Promise ' غير قابل للتعيين إلى النوع 'number'.
كيف يمكنني إصلاحه؟ كيف يمكنني جعل async await و Array.map يعملان معًا؟
الإجابة رقم 1
المشكلة هنا هي أنك تحاول await لمصفوفة من الوعود بدلًا من وعد واحد. هذا لا يفعل ما تتوقع.
عندما يكون الجسم الممرّر إلى await ليس Promise، فإن await ببساطة يُعيد القيمة كما هي فورًا بدلًا من محاولة حلها. لذلك، بما أنك مررت إلى await مصفوفة (من كائنات Promise) هنا بدلًا من Promise، فإن القيمة التي يُعيدها await هي ببساطة تلك المصفوفة، والتي تكون من النوع Promise<number>[].
ما ترغب على الأرجح في القيام به هو استدعاء Promise.all على المصفوفة التي تم إرجاعها بواسطة map من أجل تحويلها إلى Promise واحد قبل await عليها.
وفقًا لمستندات MDN لـ Promise.all:
طريقة
Promise.all(iterable)تُرجع وعدًا يتم حله عندما يتم حل جميع الوعود في الوسيط القابل للتكرار، أو يتم رفضه بسبب سبب أول وعد مَرَّ تم رفضه.
إذن في حالتك:
var arr = [1, 2, 3, 4, 5];
var results: number[] = await Promise.all(arr.map(async (item): Promise<number> => {
await callAsynchronousOperation(item);
return item + 1;
}));
سيؤدي هذا إلى حل الخطأ المحدد الذي تواجهه هنا.
اعتمادًا على ما تحاول فعله بالضبط، قد تفكر أيضًا في استخدام Promise.allSettled أو Promise.any أو Promise.race بدلاً من Promise.all، على الرغم من أنه في معظم الحالات (بما في ذلك على الأرجح هذه الحالة) سيكون Promise.all هو الذي تريد استخدامه.
الإجابة رقم 2
هذه أبسط طريقة للقيام بذلك.
await Promise.all(
arr.map(async (element) => {
....
})
)
الإجابة رقم 3
الحل أدناه لاستخدام async await و Array.map معًا بشكل صحيح. معالجة جميع عناصر المصفوفة بشكل متوازي وغير متزامن وأيضًا الحفاظ على الترتيب:
const arr = [1, 2, 3, 4, 5, 6, 7, 8];
const randomDelay = () => new Promise(resolve => setTimeout(resolve, Math.random() * 1000));
const calc = async n => {
await randomDelay();
return n * 2;
};
const asyncFunc = async() => {
const unresolvedPromises = arr.map(calc);
const results = await Promise.all(unresolvedPromises);
document.write(results);
};
document.write('calculating...');
asyncFunc();أيضًا كود بن.
لاحظ أننا نستخدم فقط "await" مع Promise.all. نحن نستدعي calc دون "await" عدة مرات، ونقوم بجمع مصفوفة من الوعود غير المحلولة على الفور. بعد ذلك، ينتظر Promise.all حل جميع هذه الوعود ويعيد مصفوفة بالقيم المحلولة بالترتيب.
الإجابة رقم 4
هناك حل آخر لذلك إذا لم تكن تستخدم وعود Native بل Bluebird.
يمكنك أيضًا محاولة استخدام Promise.map()، بمزج array.map و Promise.all
في حالتك:
var arr = [1,2,3,4,5];
var results: number[] = await Promise.map(arr, async (item): Promise<number> => {
await callAsynchronousOperation(item);
return item + 1;
});
الإجابة رقم 5
إذا قمت بعمل خريطة لمصفوفة من الوعود، يمكنك بعد ذلك حلها جميعًا إلى مصفوفة من الأرقام. انظر Promise.all.
الإجابة رقم 6
يمكنك استخدام:
for await (let resolvedPromise of arrayOfPromises) {
console.log(resolvedPromise)
}
https://developer.mozilla.org/ar/docs/Web/JavaScript/Reference/Statements/for-await...of
إذا كنت ترغب في استخدام Promise.all() بدلاً من ذلك، يمكنك استخدام Promise.allSettled() حتى تتمكن من الحصول على سيطرة أفضل على الوعود المرفوضة.
https://developer.mozilla.org/ar/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
