Приклад матеріалу для рев’ю · Мар’ян Придатко

Повільніша відповідь стирає завершене оновлення

Цей розбір використовує окремий приклад зі статті й показує, як я хочу організовувати рев’ю. Це не експорт приватного pull request.

Вимога: запити різних робіт мають зберігати обидва успішні результати незалежно від порядку завершення.

Простежити збій

Два запити, одна спільна колекція
КрокЗастарілий знімокПоточна колекція
A і B починаютьсяОбидва URL порожніОбидва URL порожні
B завершуєтьсяA порожній; B заданийA порожній; B заданий
A завершуєтьсяA заданий; B стертийA заданий; B збережений

Обидва callbacks захопили колекцію з порожніми URL. B оновлюється першим. Коли A будує результат зі старої колекції, він повертає порожній URL для B.

Зміна для рев’ю

Callback має читати колекцію в момент застосування оновлення. Логіка map залишається тією самою.

До: map по захопленому знімку
const initial = artworks;

// Inside the completion callback:
artworks = initial.map((artwork, position) =>
  position === index ? { ...artwork, url } : artwork
);
Після: map по поточній колекції
artworks = artworks.map((artwork, position) =>
  position === index ? { ...artwork, url } : artwork
);

Це присвоєння змінній є невеликою моделлю. У React відповідна зміна використовує функціональне оновлення стану, як у розборі галереї.

Запустити перевірку

Завантажити media-update.test.ts, потім запустити через Bun:

bun test ./media-update.test.ts
Прочитати повний тест
import { expect, test } from 'bun:test';

test('a slower request preserves the result that arrived first', async () => {
  let artworks = [
    { id: 'a', url: '' },
    { id: 'b', url: '' },
  ];
  const a = Promise.withResolvers<string>();
  const b = Promise.withResolvers<string>();

  const pending = [a, b].map((request, index) =>
    request.promise.then(url => {
      artworks = artworks.map((artwork, position) =>
        position === index ? { ...artwork, url } : artwork
      );
    })
  );

  b.resolve('/b.webp');
  await pending[1];
  expect(artworks).toEqual([
    { id: 'a', url: '' },
    { id: 'b', url: '/b.webp' },
  ]);

  a.resolve('/a.webp');
  await pending[0];
  expect(artworks).toEqual([
    { id: 'a', url: '/a.webp' },
    { id: 'b', url: '/b.webp' },
  ]);
});

Тест спочатку завершує B й перевіряє результат, поки A очікує. Потім завершує A та перевіряє обидва URL. Щоб відтворити баг, додайте const initial = artworks; перед const pending та замініть artworks = artworks.map на artworks = initial.map.

Що це встановлює

Робочий приклад проходить. Версія із застарілим знімком падає на останній перевірці, бо URL для B стає порожнім. Обидві версії запускалися на Bun 1.4.2.

Це перевірка колекції з незмінним складом і незалежними оновленнями різних елементів. Вона не перевіряє зміну порядку колекції, два запити одного елемента, рендеринг React, завершення дії URL або демонтування. Для них потрібні окремі випадки.