Yes, runtime.getContexts() API can do the same thing as Clients API.
let contexts = await chrome.runtime.getContexts({contextTypes: ["OFFSCREEN_DOCUMENT"]});
if (contexts.length === 0) {
// not exist
} else {
// already exist
}
However, in practice, whether runtime.getContexts() or Clients api is used, there is still a problem with the sample code given earlier.
// sample code
async function sendMessageToOffscreenDocument(data) {
// Create an offscreen document if one doesn't exist yet
if (!(await hasDocument())) {
await chrome.offscreen.createDocument(...);
}
// Now that we have an offscreen document, we can dispatch the message.
chrome.runtime.sendMessage(data);
}
I think you've guessed that there is a concurrency issue here during creation.
await hasDocument() typically takes 1 ms, an async light work.
await chrome.offscreen.createDocument(...) typically takes more than 100 ms, an async heavy work.
If you call above function multiple times at the same time, e.g.
for (let i = 0; i < 10; i++) {
sendMessageToOffscreenDocument();
}
It will throw Error: Only a single offscreen document may be created.
There are several ways to avoid this problem. For example,
let creating;
async function setupOffscreen() {
let contexts = await chrome.runtime.getContexts({contextTypes: ["OFFSCREEN_DOCUMENT"]});
if (contexts.length === 0) {
if (creating) {
console.log('already creating');
await creating;
} else {
console.log('create');
creating = chrome.offscreen.createDocument(...);
await creating;
creating = null;
}
} else {
console.log('it exists');
}
}
async function sendMessageToOffscreenDocument(data) {
await setupOffscreen();
chrome.runtime.sendMessage(data);
}
// test
for (let i = 0; i < 10; i++) {
setupOffscreen();
}