Promise-compatible (returns promise when callback omitted)