Which functions are async
Any function that does file I/O or network I/O needs to be async.
Likewise, any UI prompt that waits for user input before continuing needs to be async.
Some operations may need to be async. For example, a move() or delete() operation that works on a remote server should be async. That allows the caller to know when the operation finished, and avoids more complicated APIs where the caller needs explicitly check when and if the operation succeeded.
This implies that even generic interfaces need to be async where only one implementation needs to be async.
Given that async methods require much more code, and additional indentation, we should try to avoid making generic interfaces async. Instead, APIs should be designed so that only few load() functions are async and the rest are sync.
Luckily, collection observers remove a lot of the need for async functions. Usually, only the load() function needs to be access and none of the functions that access the list and individual objects. See section Storage for examples.
async function signatures
Every async function should always have a success path with result (but that result may be null), or an error path with a JS exception. Exactly one of these paths must be invoked. It is an error of the caller, if neither success nor error is called, and if both success and error is called. (Error path may be called several times, though?)
Code that follows the success path should be written with exactly one indention (e.g. 2 spaces) more than the call to the async function. The error path should be after the success path.
We can create utility functions to make calling async Promise functions more convenient.