Скоўваць аплодаванне калькулярных вікон з API Web Locks
Зупніце дублюванне аплодаванне файлаў у розныя вікна браузера, заменіўшы методы localStorage і BroadcastChannel на navigator.locks адносна коордынатора аплодаванняў у React.
Канкерэнцыя означае адкрытты працю над калькамі заданняў адразу — або такое вражэнне, — каб програмнае забезпечэння заставалася рэагуючым. Кухар, які нарывае інгрыянты, пакалівае ў адной сковородзе і запекае ў іншай, одночасна ведае калькі заданняў, а не завершае стравы строго адна за іншай.
Раней праця з фронтэндам выглядала далёкаю ад класычных багатапраменных сістэм з блакамі, семафорамі і застрэйкамі. Сучасная веб-платформа змяніла гэта. Завдака Worker, WebGL, Canvas і Service Worker прыкладнікі браузера цяпер сталквуюцца з проблемамі коордынацыі, якія дужа нагадваюць супербегу з аднародным памяцю.
Проблема
Команда з інжынерыі рынка патрабавала надзеяных способаў аплоаду вельмі большых файлаў — па велічыне дзесяткаў гігабайтаў, да апошней часу аж 20 ГБ. Запрашанне людзей заставляць вікно ачытым працоўна ўвесь час перадачы ёсць нестабільным. Метай продукту быў аплоад, які можа быць практыкуемы зноў: закрыцце і паўтарны ачынэнне вікна должны продаваць перадачу без неабходнасці ручнага пачатку.
Магчымасць практыкуемасці прыводзяць да новага спосабу абыектаў. Калі калькі вікна вярнуць той самы аплоад, які ўжо ідзе, кожна з іх можа пачаць перадачу незалежна. Тады хранальнік об’ектаў прыме дублікаты пакетаў, вітрачаецца пропускна здатнась, вырастаюць вартасці апрацоўкі, а інтэрфейс стану можа стварыць прамешчанне, калі вікна борацца за тую ж задачу.
Складна частка заключаецца ў тым, што кожны вікнічак сам па сабе выкаанае ўсё цяперашнім чынам. Функцыя вярнення сесіі знаходзіць непূраны запіс аплоду, кліент продовжвае мнагафрагментаваны перадачу, а інтэрфейс паказвае прыбыль. Якшча такое становішча вінікае з двума адкрытымі вікнічакамі, той самы «цяперашні» падход стае сітуацыёю, калі два працоўнікі прабуюць зместыцца з адной ключоўкай удаленага об’екта.
Без коордынатора два вярнутыя вікнічакі абоўсюды смяркаюцца, што яны ёсць власнікамі даных для аплоду, і оба прабуюць выканаць перадачу. Для рашэння проблемы неабходна взаімная эксклюзыя между вікнічакамі; прыгледвачы рэалізуюць гэты механізм пад назвай Web Locks.
Перш чым перэйсці да дэтаўнасцяў рэалізацыі, корыстна прыгадаць, для чаго вікорыстоўваецца блокіраванне.
Спачатку, што такое блокіраванне?
Блокіраванне серыялізуе доступ да спакойнага рэсурса, так што корыстувачы бачаць аднаковы стан. У класычным прыкладзе банку калькюлятары адразу чытаюць і запішваюць той самы залішок на рахунку. Без сінхронізацыі зняткі і дапісвы выкааняюцца небезпечна, паўзайчы адны другіх.
За дапамою замка першыя, хто яго атрымлівае, выконваюць свае чытанне або запіс; іншыя чакаюць, пакуль замок не будзе зваліваны. Тая ж ідэя паўтараецца ў правіле «толькі адна вікно можа продактуваць гэты завантажэння».
Ландшафт коордынаціі межа вікнамі
Некалькі старэйшых патэранаў браузера намагаюцца коордынаваць вікна. Яны працуюць, пакуль не з’яўляюцца супернаганкі, збоі або бягі протакола.
Варыянт 1: localStorage + Polling
Паўзучы мютэкс, які часта создаецца самім корыстнікам, зберагае флажок у localStorage:
// Simplified example - DON'T use this
if (!localStorage.getItem('upload-lock')) {
localStorage.setItem('upload-lock', Date.now());
// Start upload
}
Проблемы з’яўляюцца быстра:
- Умовы супернаганкі: чытанне і запіс не ўжо атымальныя
- Застарэлыя замкі: збоўлене вікно залішае флажок увесь час у актыўным стане
- Надтроцы ад polling: іншыя вікна павінны постаўляць пераканальвання
- Абмежэнні пад час адбывання зьведчанняў: зьведчанні пры збераганні адбываюцца ў іншых вікнах, а не у там, якае запісвае
Варыянт 2: API Broadcast Channel
Табы можаць весты пераказванне мэседжаў между дакументамі за дапамою BroadcastChannel:
const channel = new BroadcastChannel('upload-coordination');
channel.postMessage({ type: 'REQUEST_UPLOAD_PERMISSION' });
Ўсё застаецца прыбліжна такім:
- Няма прымітыву мюкса: вам павіннаеся самім стварыць машыну станоў
- Умовы супернічання: перакрытыя мэседжы заўсёды ствараюць канфлікты
- Складны пратакол: функціі такія як перацёрквы, выкліканне краху і правіла разрэшэння канфліктов трэба рэалізаваць вручную
- Склоннасць да памылак: часта з’яўляюцца багі, связаныя з порадкам і часам адправкі мэседжаў
Праявляецца API Web Locks: мюкс, які ўжо ёсць ў прыглядары
Веб-лакі, які ўжо ёсць ў прыглядары, даўаюць вам атомічны мюкс. Серэд корыстных якостэй —:
- Атомічнае захопленне лака — узбегае супернічання з localStorage
- Автаматычная чыстка — закрыцце або крах таба вызвалівае звалечэнне лака
- Неблокуючыя опцыі — дазволяюць UI заставацца чутлівым да спраў карыстувальніка
Наша рэалізацыя: useUploadCoordinator
Спецыяльны хук React абгружае стратэгію керавання працэсам аплодавання.
Асновная стратэгія
Інтэрактыўныя запускі трэба аднарадзіваць ад безшумных вярненняў. Калі кто-то явна выбірае файл, лепш выкарыстаць безблокіруючы спосаб аптэмбавання, каб інтэрфейс не чакаў на іншую вікно. Калі блокаванне ўвольненае, яго трэба захопіць і аплодаваць; калі ён ужэ у владзе іншага, рашэнне трэба прыміць на аднойчына на аснове правіл продукту, а не заморажваць старонку.
Пры вярненні аплодаванняў трэба прыменяць строгія правіла: толькі першая вікно, якае знову захопляе кераванне, можа продактываць перадачу. Пасляўшыя вікна, якія бачаюць, што блокаванне недоступнае, трэба заліць існуючага власніка без стварэння другога стрыму.
// 1. Fresh uploads (user-initiated): Non-blocking
// Try to get the lock, but proceed regardless
navigator.locks.request(LOCK_NAME,
{ ifAvailable: true },
async (lock) => {
if (lock) {
// Hold the lock until upload completes
await holdLockUntilComplete(abortController);
}
// Upload proceeds either way - never block the user
}
);
// 2. Restored uploads: Conditional
// Only proceed if we get the lock
navigator.locks.request(LOCK_NAME,
{ ifAvailable: true },
async (lock) => {
if (lock) {
uploader.emit("restore-confirmed"); // Proceed with restore
await holdLockUntilComplete(abortController);
}
// If no lock, skip silently - another tab is handling it
}
);
Ключовыя дэталі рэалізацыі
Утрымвайце блаканьцы пакуль не завершыцца перадача або не будзе анульвана:
const holdLockUntilComplete = (abortController: AbortController) => {
return new Promise<void>((resolve) => {
const cleanup = () => {
// Remove all listeners and resolve (releasing the lock)
uploader.off("complete", cleanup);
uploader.off("error", cleanup);
uploader.off("cancel-all", cleanup);
resolve();
};
// Release lock on any terminal event
uploader.on("complete", cleanup);
uploader.on("error", cleanup);
uploader.on("cancel-all", cleanup);
// Also release if component unmounts
abortController.signal.addEventListener("abort", cleanup);
});
};
Адказваеце на падтрымку і безпечна працавайце, калі API не ўсёродзе:
const checkWebLocksAvailable = (): boolean => {
return typeof navigator !== 'undefined' && 'locks' in navigator;
};
// Fallback behavior
if (!checkWebLocksAvailable()) {
// Allow restores to proceed (accept risk of duplicates)
uploader.emit("restore-confirmed");
return;
}
Корыстнае прыменэнне на практыцы
Пасля запуску коордынатора команда зафіксавала:
- Нуль дублікатных заваношэнняў з-за супербегу пракрэсаў пад час вярнення дадзенаў
- Лепшыя умовы для корыстніка — коордынацыя не замаражвала інтэрфейс
- Скорачанне вітакаў на інфраструктурэ — менш дублікатных перадач і збераганых об’ектаў
- Простэйшы кодавая база — без ціклаў паллінгу чы ручна створаных машын стаўяння
Толькі той пракрэс, які выграе борбу за блаканьцы, продовжае заваношэнне; прасрачаныя пракрэсы фіксуюць, што блаканьцы утрымваны, і прыпыняюць сваю дзеяльнасць.
Аспекты, якія трэба вазьмуць у ўвагу
Калі вжываць гэты патэрн:
- Заваношэння файлаў, якія вярнаюцца пасля перзагрузкі
Уплыв на выдатнасць:
- Мінімальны — няма цыклу запытанняў
- Автаматычная чыстка обмежвае колькасць „заплутаных“ процэсаў
- Запыты, якія не блакуюць, падтрымліваюць шырокую адказнасць
Стратэгія тэставання:
- Перапрацаваць кожны падтрымваны прыстрой, які вы выклікаеце
- Сімулявацыя раптовых збоёў вікноў пад час утрымвання блаку
- Паўнастаць у паведанні пры немагчымасці вікорыстоўвання Web Locks
Вывык
Коордынацыя між вікнамі не выкалікае патрэбы ў кранглавам спецыяльным пратаколе. Апыніцеся на мютэксе Web Locks платформы: ён чыстаецца праз выход з вікна і падтрымлівае неблакучыя запыты. Дапамога дакументацыі Web Locks даўа падробнасці пра API.
Практычныя правілы, якія працавалі ў працэсе вырабоцтва:
- Ніколы не блакуце дзеянні корыстніка — валіце неблакуючыя замкі для жэстоў, якія пачынае корыстнік
- Безпечная адмова — падвышайце якасць працы замест таго, каб цэлком зламаць аплодаванні
- Залічвайце простата — выкорыстоўвайце прымітывы платформы замест таго, каб іх перазбудаваць
Этыя тры правілы маюць большое значэнне, чым назва якога-небудзь конкрэтнага дапаможніка. Адто сложны коордынатор, які замаражоўвае пацёг кліка, будзе працаваць гэрш, чым простэйшы альтэрнатыўны варыант, які часам просіць корыстніка прабаваць знову. Таксама, хыткі протакол карэспандэнція, які забывае прачышчанне пасля збою, зноў стварыць проблему застарэлага замка, якая спрычыніла труднасці з localStorage.
Той жа схема прыменяецца кожны раз, калі неабходна выбора аднаго власніка для спяльной працы, не толькі для большых заваносяў. Бэкграундныя чэргі сінхронізацыі, прыстроі выкарыстоўвання дадзеных па з’яўленні і скрыпты міграцыі адной раз прыносяць парадак на тую ж схему «адны пераможнік, многі вважлівыя прағраматары».