← Усі задачіNode.js · Senior
Черга задач з ретраями й dead letter
Напишіть createQueue({ handler, retries, backoffMs }). push(job) ставить задачу в чергу й повертає проміс із результатом handler(job, attempt). Задачі виконуються по одній, у порядку додавання.
Якщо handler кидає помилку, задача повторюється до retries разів із паузою backoffMs, потім backoffMs * 2, backoffMs * 4... (експоненційна затримка). Коли спроби скінчилися, проміс відхиляється останньою помилкою, а задача потрапляє в deadLetter(). Помилка однієї задачі не зупиняє чергу.
Ctrl+Enter - запустити тести. Tab - відступ; щоб вийти з редактора клавіатурою, натисніть Esc, а потім Tab. Код зберігається в цьому браузері.
Тести (6)
const q = createQueue({ handler: async (job) => job.n * 10, retries: 2, backoffMs: 5 }); expect(await q.push({ n: 4 })).toBe(40); expect(q.deadLetter()).toEqual([]);
let calls = 0; const q = createQueue({ handler: async () => { calls++; if (calls < 3) throw new Error('busy'); return 'done'; }, retries: 3, backoffMs: 5 }); expect(await q.push('job')).toBe('done'); expect(calls).toBe(3);
const handler = fn(async () => { throw new Error('bad payload'); }); const q = createQueue({ handler, retries: 2, backoffMs: 5 }); await expect(q.push({ id: 7 })).rejects.toThrow('bad payload'); expect(handler).toHaveBeenCalledTimes(3); expect(q.deadLetter()).toEqual([{ id: 7 }]);
const times = []; const start = Date.now(); const q = createQueue({ handler: async () => { times.push(Date.now() - start); throw new Error('x'); }, retries: 3, backoffMs: 20 }); await expect(q.push(1)).rejects.toThrow('x'); const gaps = times.slice(1).map((t, i) => t - times[i]); expect(gaps[0] >= 15 && gaps[1] >= 35 && gaps[2] >= 75).toBe(true);
const order = []; let active = 0; let maxActive = 0; const q = createQueue({ handler: async (job) => { active++; maxActive = Math.max(maxActive, active); await wait(10); order.push(job); active--; return job; }, retries: 0, backoffMs: 5 }); await Promise.all([q.push('a'), q.push('b'), q.push('c')]); expect(order).toEqual(['a', 'b', 'c']); expect(maxActive).toBe(1);
const q = createQueue({ handler: async (job) => { if (job === 'bad') throw new Error('nope'); return job; }, retries: 1, backoffMs: 5 }); const bad = q.push('bad').catch((e) => e.message); const good = q.push('good'); expect(await bad).toBe('nope'); expect(await good).toBe('good'); expect(q.deadLetter()).toEqual(['bad']);