broadcast-channel
Version:
A BroadcastChannel that works in New Browsers, Old Browsers, WebWorkers, NodeJs, Deno and iframes
94 lines (83 loc) • 2.79 kB
JavaScript
import {
randomToken
} from './util.js';
import {
sendLeaderMessage,
beLeader
} from './leader-election-util.js';
/**
* A faster version of the leader elector that uses the WebLock API
* @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Locks_API
*/
export const LeaderElectionWebLock = function (broadcastChannel, options) {
this.broadcastChannel = broadcastChannel;
broadcastChannel._befC.push(() => this.die());
this._options = options;
this.isLeader = false;
this.isDead = false;
this.token = randomToken();
this._lstns = [];
this._unl = [];
this._dpL = () => { }; // onduplicate listener
this._dpLC = false; // true when onduplicate called
this._wKMC = {}; // stuff for cleanup
// lock name
this.lN = 'pubkey-bc||' + broadcastChannel.method.type + '||' + broadcastChannel.name;
};
LeaderElectionWebLock.prototype = {
hasLeader() {
return navigator.locks.query().then(locks => {
const relevantLocks = locks.held ? locks.held.filter(lock => lock.name === this.lN) : [];
if (relevantLocks && relevantLocks.length > 0) {
return true;
} else {
return false;
}
});
},
awaitLeadership() {
if (!this._wLMP) {
this._wKMC.c = new AbortController();
const returnPromise = new Promise((res, rej) => {
this._wKMC.res = res;
this._wKMC.rej = rej;
});
this._wLMP = new Promise((res) => {
navigator.locks.request(
this.lN,
{
signal: this._wKMC.c.signal
},
() => {
// if the lock resolved, we can drop the abort controller
this._wKMC.c = undefined;
beLeader(this);
res();
return returnPromise;
}
).catch(() => { });
});
}
return this._wLMP;
},
set onduplicate(_fn) {
// Do nothing because there are no duplicates in the WebLock version
},
die() {
this._lstns.forEach(listener => this.broadcastChannel.removeEventListener('internal', listener));
this._lstns = [];
this._unl.forEach(uFn => uFn.remove());
this._unl = [];
if (this.isLeader) {
this.isLeader = false;
}
this.isDead = true;
if (this._wKMC.res) {
this._wKMC.res();
}
if (this._wKMC.c) {
this._wKMC.c.abort('LeaderElectionWebLock.die() called');
}
return sendLeaderMessage(this, 'death');
}
};