You can pass resolve
and reject
to whatever asynchronous function you want to use. And such function can call it whenever it is done doing its work. Here is an example runnable in Node. If you run this, it will execute ls -l
in your current directory. The execSomething
function just takes callbacks and the promiseToExec
function passed the resolve, reject
callbacks to execSomething
rather than call either of them immediately.
const childProcess = require("child_process");
function execSomething(command, options, onSuccess, onError) {
childProcess.exec(command, options, (err, stdout, stderr) => {
if (err) {
onError(err);
}
onSuccess(stdout, stderr);
});
}
function promiseToExec(command, options) {
return new Promise((resolve, reject) => {
execSomething(command, options, resolve, reject);
});
}
promiseToExec("ls -l").then(console.log.bind(console));
Kazlauskis suggested doing this:
var resolve;
var promise = new Promise(function(fulfill) {
resolve = fulfill;
});
Don't do this!.
When an exception happens in the callback you pass to new Promise
, the specification for promises is such that the exception will automatically be converted into a promise rejection. So if anything does throw Error...
inside the callback you get automatic conversion.
If you save the resolve
callback and move your logic outside of the callback you pass to new Promise
, then you do not get this automatic conversion. An exception thrown outside the callback will just be passed up the stack without being converted to a promise rejection. This is bad because it requires users of your function to use .catch
to catch rejected promises and try...catch
for thrown exceptions. This is a bad design practice.
Here's code illustrating the issue:
// This is how things should be done.
function makeGoodPromise(num) {
return new Promise((resolve) => {
if (num < 0) {
throw new Error("negative num");
}
resolve(num);
});
}
// This is a bad approach because it will sometimes result in synchronous
// exceptions.
function makeBadPromise(num) {
let resolve;
const p = new Promise((fullfil) => {
resolve = fullfil;
});
if (num < 0) {
throw new Error("negative num");
}
resolve(num);
return p;
}
// Shoring up the bad approach with a try... catch clause. This illustrates what
// you need to do convert the exception into a rejection. However, why deal with the
// additional scaffolding when you can just take the simpler approach of not
// leaking the callbacks??
function makeBadPromise2(num) {
let resolve, reject;
const p = new Promise((fullfil, deny) => {
resolve = fullfil;
reject = deny;
});
try {
if (num < 0) {
throw new Error("negative num");
}
resolve(num);
}
catch (e) {
reject(e);
}
return p;
}
makeGoodPromise(-1).catch(() => console.log("caught the good one!"));
try {
makeBadPromise(-1).catch(() => console.log("caught the bad one!"));
}
catch(e) {
console.log("Oops! Synchronous exception: ", e);
}
makeBadPromise2(-1).catch(() => console.log("caught the bad2 one!"));
When I execute it in Node, this is the output:
Oops! Synchronous exception: Error: negative num
at makeBadPromise (/tmp/t12/test2.js:17:11)
at Object.<anonymous> (/tmp/t12/test2.js:48:3)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:394:7)
at startup (bootstrap_node.js:149:9)
caught the good one!
caught the bad2 one!