7 ms·
For a practical example, take this code I wrote recently:[1] const backends = {} async function setupBackend(host) { /* boot server */ } func
by wolfgang42 5y ago
For a practical example, take this code I wrote recently:[1]
const backends = {}
async function setupBackend(host) { /* boot server */ }
function getBackend(host) {
if (!backends[host]) {
backends[host] = setupBackend(host)
}
return backends[host]
}
That if block is effectively a critical section: it relies on explicitly not awaiting the result of setupBackend(), so that the promise will be stored into backends[host] (to be reused by subsequent calls to getBackend()) before anything else can happen. Injecting `async` will break this behavior, causing the backend to be booted multiple times.
[1] https://github.com/wolfgang42/webd/blob/8cf28447468dd4745262800ec0fc5a6e9da2aa09/webd.js#L127-L132 https://github.com/wolfgang42/webd/blob/8cf28447468dd4745262...
- pkage 5y agoWhy not simply set some intermediate value to indicate that the backend is booting? Such as: const backends = {} async function setupBackend(host) { /* boot server */ } function getBackend(host) { if (!backends[host]) { backends[host] = 'FLAG' // or something setupBackend(host) .then(backend => backends[host] = backend) } else if (backends[host] === 'FLAG') { // no-op } else { return backends[host] } }
- wolfgang42 5y agoSubsequent callers of getBackend also need to be able to await on the backend boot before they can do anything useful. If the backend is currently booting, your function will return undefined in the “no-op” case, giving the caller no way to tell when it’s finished (other than retrying, I suppose). I tried writing some code for this comment that used an explicitly constructed Promise as the intermediate value with some logic to resolve it once setup was complete, but then I realized that it had exactly the same problem with that being implicitly waited on. Maybe there’s some clever way to work around this but it’s going to be a lot more complicated. Of course, if you do insist on a JS dialect with implicit await, the easy fix for this problem (since you’re transpiling anyway) is to just introduce a `noawait` keyword that turns the await insertion off for a block, to explicitly mark it as atomic. [ETA: also, in the general case there’s a race condition if someone calls the function again while it’s awaiting the initial flag value. That doesn’t happen with your code because the OP library happens to not await literals, but that’s kind of fragile: I can easily see a situation where someone tries to introduce e.g. a counter into the flag and causes a non-obvious race.]