new Promise(executor) calls the executor immediately and synchronously with two functions: resolve(value) fulfills the promise (or makes it follow another promise) and reject(reason) rejects it, as does a throw in the executor. Neither returns from the executor, only the first call counts, and then() handlers always run later.
const p = new Promise((resolve, reject) => {
resolve("done");
reject(new Error("ignored")); // only the first call counts
console.log("1 the executor runs at once; resolve() did not return");
});
p.then(value => console.log("3 fulfilled:", value));
console.log("2 after the constructor");
new Promise(() => { throw new TypeError("bad input"); }).catch(err => console.log("4", err.name));1 the executor runs at once; resolve() did not return 2 after the constructor 3 fulfilled: done 4 TypeError
Creating a promise starts the work, so wrap it in a function to start it later, like sleep() in Practical Asynchrony. Node's util.promisify(fn) wraps any error-first callback API.