Consider the following code snippet:
let primise = new Promise((resolve, reject) => {
resolve({ x: 10 });
});
setTimeout(() => {
// At some moment in the future (Promise is resolved)
console.log(promise);
}, 200);
Now, the promise was resolved with { x: 10 }. How do I access this data using the resolved promise ?
Inspecting the promise object I can see the data is available as promise._v but this doesn't look right. Any help would be appreciated
No, you cannot inspect promises (at least not native promises, some userland implementations do have synchronous inspection methods available).
So - as always - use promise.then(v => console.log(v)). Only then you can be sure that the promise is fulfilled.
If you also want to wait 200ms, use another promise for the timeout and Promise.all to wait for both of them:
let promise = Promise.resolve({ x: 10 });
Promise.all([promise, new Promise(resolve => setTimeout(resolve, 200))]).then([v] => {
// At some moment in the future (and promise is fulfilled)
console.log(v); // {x: 10}
});
The whole point of a promise is so that you don't have to wait an arbitrary amount of time to determine whether the promise is resolved... you just hook into it.
let promise = new Promise((resolve, reject) => {
resolve({ x: 10 });
});
...
promise.then(data => {
// use data here...
});
Related
I have a situation in my node.js program where I have an array of promises. I am prepared to wait a maximum of 200 ms for each promise in the array to get fulfilled, if it’s not fulfilled by then I want it to be rejected.
The code I have written for this works when I run my script in the terminal using node.js without a debugger attached.
However, when I debug the same script using VS code it stops as soon as a promise gets rejected due to timeout. The debugger claims that the rejection is an uncaught exception.
How can I change the code I have such that it does exactly what it does now, but a rejected promise doesn’t cause an exception?
I have tried adding try{} catch{} all over the place but cannot seem to find a solution.
Here is a minimal reproducible example of my issue (the debugger complains about the line reject( "timeout" ) ):
async function delayedPromise(delay) {
await new Promise((res) => setTimeout(res, delay));
return "success";
}
function rejectAfterDelay(ms) {
return new Promise((_, reject) => setTimeout(() => {
reject("timeout");
}, ms));
}
async function main() {
// Create array of promises.
promArr = [];
promArr.push(delayedPromise(100));
promArr.push(delayedPromise(200));
promArr.push(delayedPromise(300));
promArr.push(delayedPromise(400));
promArr.push(delayedPromise(500));
// Wait for all promises to either get fulfilled or get rejected after 200 ms.
const msMaxTime = 200;
const result = await Promise.allSettled(
promArr.map(promise => Promise.race([promise, rejectAfterDelay(msMaxTime)]))
);
console.log(result);
}
main()
Instead of racing a promise with a short-lived promise(rejectAfterDelay), we can wrap the promise in a short-lived promise:
async function delayedPromise(delay) {
return new Promise((res) => setTimeout(res, delay, 'success'));
}
// wrap the promise instead of racing it
function rejectAfterDelay(promise, ms) {
return new Promise((resolve, reject) => {
setTimeout(reject, ms, 'timeout');
// forward the reasons to the wrapper
promise.then(reason => resolve(reason))
.catch(err => reject(err));
});
}
async function main() {
// Create array of promises.
promArr = [];
promArr.push(delayedPromise(100));
promArr.push(delayedPromise(200));
promArr.push(delayedPromise(300));
promArr.push(delayedPromise(400));
promArr.push(delayedPromise(500));
// Wait for all promises to either get fulfilled or get rejected after 200 ms.
const msMaxTime = 200;
const result = await Promise.allSettled(
promArr.map(promise => {
//return Promise.race([promise, rejectAfterDelay(msMaxTime)]);
return rejectAfterDelay(promise, msMaxTime);
})
);
console.log(result.map(r => r.value ? r.value : r.reason));
}
main()
With this the debugger doesn't complain when Uncaught Exceptions option is selected.
Also, depending on your situation, instead of setTimeout(reject, ms, 'timeout') you can use setTimeout(resolve, ms, 'timeout') to make it fail gracefully.
This question already has answers here:
NodeJS Timeout a Promise if failed to complete in time
(8 answers)
Wait until all promises complete even if some rejected
(20 answers)
Closed 2 years ago.
I have a unique situation where 2 promises are running together in Promise.all.
but one of the promise is taking long time and hence I am not getting any result.
Other promise are getting resolved except one.
I want to reject the promise taking long time (eg: if more than 60 sec) with an error message so that I can get a response from Promise.all.
e.g::
const [client1Prices, client2Prices] = await Promise.all([
this.client1.getSimulationPrices({
hourPay: journey.hourPay,
jobType: journey.jobType,
salary: journey.salary,
weeklyHours: journey.weeklyHours,
}),
this.client2.getSimulationPrices({ // takes more than 60 sec and i want to reject this promise
hourPay: journey.hourPay,
jobType: journey.jobType,
salary: journey.salary,
weeklyHours: journey.weeklyHours,
})
]);
this.client2.getSimulationPrices is taking a lot of time to resolve and hence Promise.all is not giving me any result. I want to reject this in 60 second such thatI can get response from Promise.all.
Please suggest how can this situation be handled ?
Use Promise.allSettled()
It return ar array of objects promise that resolves after all of the given promises have either fulfilled or rejected.
Each objects in the array describes the outcome of each promise.
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'Prmoise 2'));
const promise3 = new Promise((resolve, reject) => setTimeout(resolve, 200, 'Promise 3'));
const promise4 = new Promise((resolve, reject) => setTimeout(reject, 200, 'Promise 4'));
const promises = [promise1, promise2, promise3, promise4];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
// expected output:
// "fulfilled"
// "rejected"
// "fulfilled"
// "rejected"
More info
Promise.allSettled() - MDN
All major browsers support it. So you do not need to use any external library or even polyfills.
You are looking for Promise.allSettled(). Unlike Promise.all() it would not thrown an error if any of the promises fails. Instead the result is an object that is either:
{
status: 'fulfilled',
value: return_value
}
if there was no error or:
{
status: 'rejected',
reason: error_message
}
if there was an error.
MDN says this is supported from node 12.9.0 and above. If you need it in an environment that does not have it there is a pure-js implementation on npm: https://www.npmjs.com/package/promise.allsettled
See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
You should consider using observables as this is a bit easier to handle, however, you can still do this with promises.
You'll want to use Promise.race().
The Promise.race() method returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise.
const timeoutPromise = new Promise((resolve, reject) => setTimeout(() => reject('TIMEOUT'), 60000));
});
const [client1Prices, client2Prices] = await Promise.all([
this.client1.getSimulationPrices({
hourPay: journey.hourPay,
jobType: journey.jobType,
salary: journey.salary,
weeklyHours: journey.weeklyHours,
}),
Promise.race([timeoutPromise, this.client2.getSimulationPrices({
hourPay: journey.hourPay,
jobType: journey.jobType,
salary: journey.salary,
weeklyHours: journey.weeklyHours,
})])
]);
You are guaranteed to have client2Prices return in 60sec or less. You can check the value to determine if it timed out or if it succeeded. If you wanted to swallow the timeout error (not have Promise.all() fail, you can either make the timeout resolve (instead of reject), or switch to use Promise.allSettled()
An excellent library for Promises is bluebird.
It has a timeout function that would prove useful here.
const Promise = require('bluebird');
const [client1Prices, client2Prices] = await Promise.allSettled([
this.client1.getSimulationPrices(...).timeout(60000),
this.client2.getSimulationPrices(...).timeout(60000)
]);
I want to reject a promise which I do not built. That is, examples I've read describe something like it:
const sample = new Promise((resolve, reject) => {
setTimeout(() => {
reject('fail promise');
}, 1000);
});
That reject the sample after 1s. In my case the promise I want to reject is coming as an external api call then I can't to reject in that way.
Another approaches I've read shows how the promise can be wrapped with other that uses a setTimeout to reject the new promise. Something like this:
const timeout = new Promise(function(resolve, reject) {
setTimeout(resolve, 1000, 'one');
});
const sample = new Promise(function(resolve, reject) {
setTimeout(resolve, 5000, 'two');
});
return Promise.race([sample, timeout]);
That force to 'reject' sample after 1s returning the other promise. That could be used to set a timeout but in fact it does not reject sample, only returns another promise and the original continues running until it rejects or resolve itself.
I can not find how can I reject correctly a promise without a library
Why do you need to reject their promise? What you care about is getting a useful result for your code, so write code that gets you that result instead.
Wrap the API call in your own promise, with a timeout rejection on your promise, and a pass-through resolve if that API yields a result before the timeout:
const attempt = new Promise( (resolve, reject) => {
// run the actual API _and_ your rejection timeout concurrently:
let rejected = false;
const id = setTimeout(() => {
rejected = true;
reject(new Error('timeout');
}), 1000);
actualApiCall(some, input, args, here)
.then(result => {
if (rejected) return; // <- if we rejected via timeout, we no longer care.
clearTimeout(id); // <- always remember to properly clean up
resolve(result);
})
.catch(e => {
if (rejected) return;
clearTimeout(id);
reject(e)
});
});
attempt
.then(result => doSomethingWith(result))
.catch(e => console.log('rejected:', e));
I can not find how can I reject correctly a promise without a library
First of all, this doesn't seems a good approach to me, even though you can do the following,
const apiPromise = externalApi();
Now apiPromise can either be in resolved or rejected state. In both cases you can throw an error like,
const rejectedPromise = apiPromise.then(
(fulfilledValue) => { throw "error" },
(rejetedValue) => { throw "error" }
);
rejectedPromise.catch(err => console.log(err)); //logs "error"
Read more on Promise.then()
I'm pretty new to Node.js. I have a problem with promise functions and Promise.all.
Either I misunderstood some concept or I'm doing something wrong. I've tried researching on that topic but could not find the right answer to my specific problem.
The situation is as follows.
I have a promise function to generate a pdf, that returns the path of the generated pdf.
var myNicePdfCreator={
setting1: value1,
setting2: value2,
createPdf: function(customerId){
return new Promise(function(resolve, reject){
dataBase.fetchForId(customerId).then(function(data){
somePdfLibrary.createPdf.then(function(path){
resolve(path);
},function(err){
//error creating pdf
reject(err);
});
},function(err){
//error fetching from db
reject(err);
}
})
}
}
I have a promise function that takes an email address and path to the pdf and then sends an email with the attached pdf
var myNiceMailSender={
setting1: value1,
setting2: value2,
sendMail: function(email, path){
return new Promise(function(resolve, reject){
someMailLibrary.createMail(email, title, message, attachment).then(function(status){
resolve(status);
},function(err){
reject(err);
});
});
}
}
I want to do this for every object in an array (for example get a report for every customer and then email it to them). I was trying to come up with a promise function that first creates the pdf and then sends the mail and use a forEach loop to push the promise to an array and then use Promise.all to make all the PDFs and send out the mails. but whatever I try, whenever I push a promise to an array it already gets resolved before I even use Promise.all even if I just try pushing one of both promise functions to the array.
If i do this;
var promises=[];
AllCustomers.forEach(customer){
promises.push(myNicePdfCreator.createPdf(customer.id));
});
The PDFs are directly created when I push them to the array. I don't even need to call Promise.all.
The same if I try to push the promise function to an array that sends the emails, the mails are sent instantly.
Can anyone point me in the right direction why my promises get resolved when I push them to the array?
Is there a better way to create the PDFs and then email them?
Any help appreciated thank you!
I suppose what you want is to get all the pdfs gereated before you start sending the emails. As someone already said, when you call a Promise, unless you have a .then or an await for it, its execution is not going to wait.
const promises=[];
for(const customer of AllCustomers){
promises.push(myNicePdfCreator.createPdf(customer.id));
});
Promise.all(promises).then((paths) => {
// paths is going to have an array with the paths of all the pdfs already generated
});
With this code, in Promise.all is going to wait until all pdfs are generated. So inside the then, you could send the emails.
If you want to create an array of unresolved, unprocessing promises, which create a report for each customer and then email that customer, it would look something like this:
const pfuncs = AllCustomers.map(customer => {
return async function() {
const pdfPath = await myNicePdfCreator.createPdf(customer.id);
const status = await myNiceMailSendor.sendMail(customer.email, pdfPath);
return {status, pdfPath};
}
})
This creates an array of functions -- the 'createPdf' request hasn't started to run yet, it's waiting for you to call each function in the array.
When you're ready to send the requests, you would do
const results = await Promise.all(pfuncs.map(f => f()));
And now results is an array that looks like [{status: 200, pdfPath: ''} , {status: 200, pdfPath: ''}, ...]
The promises are executed when they are declared. If you want to "lock" all the promises till all are defined you could encapsulate in a function and after declaring all, make a loop to execute it.
// Await "a" seconds to finish, reject if "a" o "b" are negative
function myPromiseFunction(a, b) {
return new Promise((res, rej) => {
setTimeout(() => {
if (a < 0 || b < 0) {
rej(0);
} else {
res(a+b);
}
}, a * 1000);
})
}
(() => {
let someData = [{a:2,b:2}, {a:10, b:4}];
// Generate promises in functions
let myPromises = someData.map((v) => {
return () => myPromiseFunction(v.a, v.b);
});
// Execute all
myPromises = myPromises.map((promise) => promise());
// ...
})();
The Promise.all function only awaits to all the promises to finish the process or any promise is rejected. For example:
All promises good:
// Await "a" seconds to finish, reject if "a" o "b" are negative
function myPromiseFunction(a, b) {
return new Promise((res, rej) => {
setTimeout(() => {
if (a < 0 || b < 0) {
rej(0);
} else {
res(a+b);
}
}, a * 1000);
})
}
(() => {
let someData = [{a:2,b:2}, {a:10, b:4}];
// Generate promises in functions
let myPromises = someData.map((v) => {
return () => myPromiseFunction(v.a, v.b);
});
// Execute all
myPromises = myPromises.map((promise) => promise());
// Await all
Promise.all(myPromises).then(res => {
console.log(res, myPromises);
}).catch(err => {
console.log(err, myPromises);
});
})();
You will print the console.log in then and will be like this:
// res: [ 4, 14 ]
// myPromises: [ Promise { 4 }, Promise { 14 } ]
But if you have a promise who fails like this:
let someData = [{a:10,b:2}, {a:4, b:-4}, {a:2, b:4}];
The second promise will be rejected by the negative value, but the first promise will not resolve (10 seconds to end) so the output of the catch will be:
// err: 0 (this is the error of the first rejected promise)
// myPromises: [
// Promise { <pending> }, // The rejected promise will not stop the pendings
// Promise { <rejected> 0 }, // The rejected promise
// Promise { 6 }, // A "resolved" or maybe "pending"
// ]
Promises just baffle me.
I'm trying to make a mock data service to imitate axios.
My mock put call passes a targetUrl to _fetch which then sees if it's a valid url and either returns a new Promise with a delayed .resolve
const _returnResponse = (mockData, time = 0) => new Promise((resolve) => {
setTimeout(() => {
resolve(mockData);
}, time);
});
or a new Promise with a delayed .reject
const _returnError = (time = simulatedDelay) => {
const returnValue = new Promise(((resolve, reject) => {
setTimeout(() => {
reject(new Error('error'));
}, time);
}));
return returnValue;
};
but when I make my mock put call this returns a mock data that the calling method interprets as a success and console logs in its .then
put(target, putBody) {
const returnValue = _fetch(target, simulatedDelay)
returnValue.then(response => _console('PUT', target, response, putBody));
return returnValue;
},
But with an invalid target console logs an uncaught error
or this handles the error correctly, but console logs an undefined response
put(target, putBody) {
const returnValue = _fetch(target, simulatedDelay).then(response => _console('PUT', target, response, putBody));
return returnValue;
},
Here's the calling method:
saveStuff({ commit, state }, newStuff) {
//other code
return this.$mockAxios.put(url, putBody)
.then((response) => {
return response;
});
},
I feel like I'm completely missing something and I've researched this for hours and I'm still not getting it.
As a direct answer to the question: yes, you can add .then() to a promise after it's created.
Example:
const hi = new Promise((resolve, reject) => {
setTimeout(() => resolve('hello'), 2000);
});
hi.then(result => console.log(result));
As for promises baffling you, I would recommend (aside from more reading) just playing around a lot in your IDE with setTimeout. I know you're already using setTimeout in your mock, but strip it down further and just run the code in its own file, so that you control the whole environment. With lots of console.log('blah') to see the order and such. Also ensure you're just as familiar with callbacks, as promises are just syntactic sugar for callbacks. Try to read up on the JS event loop at the same time - it might provide some context if you know how and when a callback/promise executes.
Note that you can even add .then() after the callback has resolved or rejected. Hence the term "promise" - it's literally a promise that your .then() function will run. https://en.wikipedia.org/wiki/Promise_theory
const hi = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('one second');
resolve();
}, 1000);
});
setTimeout(() => {
hi.then(() => console.log('two seconds. this executes approximately a full second after the first promise has resolved'));
}, 2000);