✅
disposable-lock v2 で チェックボックスをロックするサンプル
disposable-lock v2 系では、lock(name).request() が次のように改善されました:
- ロック取得成功 →
ReleasableLockオブジェクト - ロック取得失敗(
ifAvailable: true) →null -
await usingによる自動解放対応
この記事では、チェックボックスグリッドでロックを体感できるサンプルを紹介します。
サンプルの概要
- チェックボックスをクリックすると、最初に空いているロックを取得します
- ロックが取得できない場合は操作はスキップ
- チェックを外す際にロックは解除されます
- 色でロック状況を視覚化
HTML
<div id="grid" class="grid">
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
</div>
CSS
.grid {
display: grid;
grid-template-rows: repeat(4, min-content);
grid-template-columns: repeat(4, min-content);
gap: 0.5rem;
}
.grid input {
width: 2rem;
height: 2rem;
}
.grid input[data-lock] {
accent-color: hsl(calc(var(--count) * (360 / 16) * 1deg) 80% 40%);
}
/* lock のデータ属性に応じた色付け */
[data-lock="lock:01"] { --count: 0; }
[data-lock="lock:02"] { --count: 1; }
[data-lock="lock:03"] { --count: 2; }
[data-lock="lock:04"] { --count: 3; }
[data-lock="lock:05"] { --count: 4; }
[data-lock="lock:06"] { --count: 5; }
[data-lock="lock:07"] { --count: 6; }
[data-lock="lock:08"] { --count: 7; }
[data-lock="lock:09"] { --count: 8; }
[data-lock="lock:10"] { --count: 9; }
[data-lock="lock:11"] { --count: 10; }
[data-lock="lock:12"] { --count: 11; }
[data-lock="lock:13"] { --count: 12; }
[data-lock="lock:15"] { --count: 13; }
[data-lock="lock:16"] { --count: 14; }
JavaScript
import { lock } from "disposable-lock";
import { withSafeResolvers } from "readable-stream-with-safe-resolvers";
const locks = new WeakMap();
const num = ["01","02","03","04","05","06","07","08","09","10"];
async function getLock() {
const options = { ifAvailable: true };
for (const n of num) {
const { request } = lock(`lock:${n}`);
const l = await request(options);
if (l) return l;
}
return undefined;
}
const { enqueue, stream } = withSafeResolvers();
const grid = document.getElementById("grid");
grid.addEventListener("change", enqueue);
for await (const event of stream) {
const target = event.target.closest("input");
if (!target) continue;
event.preventDefault();
// 変更を元に戻して無かったことにする
target.checked = !target.checked;
// 既存のロックがある場合は解放
const oldLock = locks.get(target);
if (oldLock) {
target.checked = false;
locks.delete(target);
oldLock.release();
delete target.dataset.lock;
continue;
}
// 新しいロックを取得
const lockObj = await getLock();
if (lockObj) {
target.dataset.lock = lockObj.name;
locks.set(target, lockObj);
target.checked = true;
}
}
ポイント解説
-
ifAvailable: true
ロックが取得できなければnullを返すので、非同期競合の際も安全に扱える -
リソースの可視化
data-lockと CSS による色付けで、どのチェックボックスが占有中かが一目でわかる
以上。
Discussion