One question I see a lot about web components is whether this is okay:
async connectedCallback() {
const response = await fetch('/data.json')
const data = await response.json()
this._data = data
}
The answer is: yes. It’s fine. Go ahead and make your connectedCallbacks async. Thanks for reading.
What? You want a longer answer? Most people would have tabbed over to Reddit by now, but sure, no problem.
The important thing to remember here is that an async function is just a function that returns a Promise. So the above is equivalent to:
connectedCallback() {
return fetch('/data.json')
.then(response => response.json())
.then(data => {
this._data = data
})
}
Note, though, that the browser expects connectedCallback to return undefined (or void for you TypeScript-heads). The same goes for disconnectedCallback, attributeChangedCallback, and friends. So the browser is just going to ignore whatever you return from those functions.
The best argument against using this pattern is that it kinda-sorta looks like the browser is going to treat your async function differently. Which it definitely won’t.
For example, let’s say you have some setup/teardown logic:
async connectedCallback() {
await doSomething()
window.addEventListener('resize', this._onResize)
}
async disconnectedCallback() {
await undoSomething()
window.removeEventListener('resize', this._onResize)
}
You might naïvely think that the browser is going to run these callbacks in order, e.g. if the component is quickly connected and disconnected:
div.appendChild(component) div.removeChild(component)
However, it will actually run the callbacks synchronously, with any async operations as a side effect. So in the above example, the awaits might resolve in a random order, causing a memory leak due to the dangling resize listener.
So as an alternative, you might want to avoid async connectedCallback just to make it crystal-clear that you’re using side effects:
connectedCallback() {
(async () => {
const response = await fetch('/data.json')
const data = await response.json()
this._data = data
})()
}
To me, though, this is ugly enough that I’ll just stick with async connectedCallback. And if I really need my async functions to execute sequentially, I might use the sequential Promise pattern or something.
