Reputation: 3016
I would like to construct a Promise, but defer resolution until later. The code below creates a promise, but it is resolved immediately. How can I control when the promise gets evaluated?
var p = new Promise((resolve, reject) => {
resolve(1);
})
.then((p1) => {
console.log(p1 + 1);
});
UPDATE: To clarify, the reason for wanting to separate the declaration of the promise from its execution is to add then
callbacks dynamically, based on some arguments.
Upvotes: 5
Views: 2693
Reputation: 139
EDIT: as Luis points out in another answer, if an exception is thrown it will not be caught by the promise constructor but leaves this handling to the outer scope. This might be desirable in some situations, but I would also recommend using constructors and passing the resolver function as a callback. More on this here: Resolve Javascript Promise outside function scope
You can:
var resolve;
var promise = new Promise(function(fulfill) {
resolve = fulfill;
});
// now you can resolve the promise whenever you want
promise.then(function() {
console.log('done!');
});
resolve();
Upvotes: -1
Reputation: 151370
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!
Upvotes: 5
Reputation: 6947
Not entirely sure what you are asking- the code below demonstrates that construction of the promise and calling then
happen in the same order, but may execute at different times. Change the values for wait1
and wait2
and see how the output is different, but the code works correctly regardless of the timing.
I think it will be up to you to implement the promise code so that it waits for whatever condition you want to wait for. In the example, it is a simple setTimeout
, but you could conceivably do anything to defer execution.
You'll probably need to use Chrome to see these results in your browser:
var wait1 = 2000;
var wait2 = 1000;
function log(...str) {
var li = document.createElement('li');
str.forEach((s) => {
li.appendChild(document.createTextNode(s));
});
document.getElementById("log").appendChild(li);
}
var p = new Promise((resolve, reject) => {
setTimeout(() => {
log("Resolving promise!");
resolve(1);
}, wait1);
});
log("Promise created!");
setTimeout(() => {
log("Calling 'then'");
p.then((p1) => {
log("Value:", p1 + 1);
});
}, wait2);
<ol id="log" />
Upvotes: 1