Futa Arai 2 лет назад
Родитель
Сommit
1b14d15119
2 измененных файлов с 37 добавлено и 4 удалено
  1. 24 1
      apps/app/src/utils/promise.spec.ts
  2. 13 3
      apps/app/src/utils/promise.ts

+ 24 - 1
apps/app/src/utils/promise.spec.ts

@@ -5,7 +5,6 @@ describe('batchProcessPromiseAll', () => {
     const batch1 = [1, 2, 3, 4, 5];
     const batch1 = [1, 2, 3, 4, 5];
     const batch2 = [6, 7, 8, 9, 10];
     const batch2 = [6, 7, 8, 9, 10];
     const batch3 = [11, 12];
     const batch3 = [11, 12];
-
     const all = [...batch1, ...batch2, ...batch3];
     const all = [...batch1, ...batch2, ...batch3];
 
 
     const actualProcessedBatches: number[][] = [];
     const actualProcessedBatches: number[][] = [];
@@ -25,4 +24,28 @@ describe('batchProcessPromiseAll', () => {
       batch3,
       batch3,
     ]);
     ]);
   });
   });
+
+  describe('error handling', () => {
+    const all = [1, 2, 3, 4, 5, 6, 7, 8, '9', 10];
+
+    const multiplyBy10 = async(num) => {
+      if (typeof num !== 'number') {
+        throw new Error('Is not number');
+      }
+      return num * 10;
+    };
+
+    describe('when throwIfRejected is true', () => {
+      it('throws error when there is a Promise rejection', async() => {
+        await expect(batchProcessPromiseAll(all, 5, multiplyBy10)).rejects.toThrow('Is not number');
+      });
+    });
+
+    describe('when throwIfRejected is false', () => {
+      it('doesn\'t throw error when there is a Promise rejection', async() => {
+        const expected = [10, 20, 30, 40, 50, 60, 70, 80, 100];
+        await expect(batchProcessPromiseAll(all, 5, multiplyBy10, false)).resolves.toStrictEqual(expected);
+      });
+    });
+  });
 });
 });

+ 13 - 3
apps/app/src/utils/promise.ts

@@ -4,21 +4,31 @@
  * @param items array to process
  * @param items array to process
  * @param limit batch size
  * @param limit batch size
  * @param fn function to apply on each item
  * @param fn function to apply on each item
+ * @param throwIfRejected whether or not to throw Error when there is a rejected Promise
  * @returns result of fn applied to each item
  * @returns result of fn applied to each item
  */
  */
 export const batchProcessPromiseAll = async<I, O>(
 export const batchProcessPromiseAll = async<I, O>(
   items: Array<I>,
   items: Array<I>,
   limit: number,
   limit: number,
   fn: (item: I, index?: number, array?: Array<I>) => Promise<O>,
   fn: (item: I, index?: number, array?: Array<I>) => Promise<O>,
+  throwIfRejected = true,
 ): Promise<O[]> => {
 ): Promise<O[]> => {
-  let results: O[] = [];
+  const results: O[] = [];
+
   for (let start = 0; start < items.length; start += limit) {
   for (let start = 0; start < items.length; start += limit) {
     const end = Math.min(start + limit, items.length);
     const end = Math.min(start + limit, items.length);
 
 
     // eslint-disable-next-line no-await-in-loop
     // eslint-disable-next-line no-await-in-loop
-    const slicedResults = await Promise.all(items.slice(start, end).map(fn));
+    const slicedResults = await Promise.allSettled(items.slice(start, end).map(fn));
 
 
-    results = results.concat(slicedResults);
+    slicedResults.forEach((result) => {
+      if (result.status === 'fulfilled') {
+        results.push(result.value);
+      }
+      else if (throwIfRejected && result.reason instanceof Error) {
+        throw result.reason;
+      }
+    });
   }
   }
 
 
   return results;
   return results;