As a native programmer who meddles with the horrors of thread safety and & and &mut, and occasionally dabbles in high-level (JS/Dart) asynchronity, async functions fill me with much of the same fear and caution. await looks like merely a nonblocking function call, but means arbitrary code executes and can and will mutate arbitrary state under your feet. Shared state across coroutines is nearly as dangerous as shared state across threads, and (I conjecture) far more pervasive.<p>In the code in question (<a href="https://web.archive.org/web/20220113153505/https://web.dev/file-system-access/#drag-and-drop-integration" rel="nofollow">https://web.archive.org/web/20220113153505/https://web.dev/f...</a>, now changed in <a href="https://web.dev/file-system-access/#drag-and-drop-integration" rel="nofollow">https://web.dev/file-system-access/#drag-and-drop-integratio...</a>):<p><pre><code> elem.addEventListener('drop', async (e) => {
// Prevent navigation.
e.preventDefault();
// Process all of the items.
for (const item of e.dataTransfer.items) {
// Careful: `kind` will be 'file' for both file
// _and_ directory entries.
if (item.kind === 'file') {
const entry = await item.getAsFileSystemHandle();
if (entry.kind === 'directory') {
handleDirectoryEntry(entry);
} else {
handleFileEntry(entry);
}
}
}
});
</code></pre>
I probably wouldn't have guessed that `e.dataTransfer.items` gets cleared at the first await (since I'm not a proficient web developer), but I would've been <i>extremely</i> wary of this code in general. Additionally (not tied to async-await but race conditions in general), is `item.getAsFileSystemHandle()` a TOCTTOU vulnerability where the type of an item can change between folders and files and symlinks etc., while this code is running?<p>Rust's & vs. &mut system largely eliminates shared state hazards in both threading and asynchronity (&mut is exclusive/unaliased and can't be mutated by other threads or event loop jobs, and & is difficult and unidiomatic to mutate), though it doesn't solve async cancellation errors (<a href="https://carllerche.com/2021/06/17/six-ways-to-make-async-rust-easier/" rel="nofollow">https://carllerche.com/2021/06/17/six-ways-to-make-async-rus...</a>, discussed at <a href="https://news.ycombinator.com/item?id=27542504" rel="nofollow">https://news.ycombinator.com/item?id=27542504</a>), or filesystem TOCTTOU (<a href="https://blog.rust-lang.org/2022/01/20/cve-2022-21658.html" rel="nofollow">https://blog.rust-lang.org/2022/01/20/cve-2022-21658.html</a> as well as user code).<p>Qt event loop reentrancy is fun(tm) as well. It looks like a blocking call, but spawns a nested event loop which can do anything (but rarely enough to lull you into a false sense of complacency), resulting in segfaults like <a href="https://github.com/Nheko-Reborn/nheko/issues/656" rel="nofollow">https://github.com/Nheko-Reborn/nheko/issues/656</a> (workaround at <a href="https://github.com/Nheko-Reborn/nheko/commit/570d00b000bd558592af317746fa3639fb022fa4" rel="nofollow">https://github.com/Nheko-Reborn/nheko/commit/570d00b000bd558...</a>, I didn't look into it). And Qt lacks "easy" await syntax and a framework based on calling red functions (though I didn't look into C++20 coroutines yet, perhaps <a href="https://www.qt.io/blog/asynchronous-apis-in-qt-6" rel="nofollow">https://www.qt.io/blog/asynchronous-apis-in-qt-6</a> or <a href="https://github.com/mhogomchungu/tasks" rel="nofollow">https://github.com/mhogomchungu/tasks</a> or <a href="https://blog.blackquill.cc/asynchronous-qtquick-uis-and-their-implementation-the-toolbox#thennables" rel="nofollow">https://blog.blackquill.cc/asynchronous-qtquick-uis-and-thei...</a>?).