Promise with array is not working as expected - javascript

I am calling 2 functions using Promise.all function, where I am passing an odd number and an even number. Based on the number I am deciding the success or failure.
const time11=(t) =>{
return new Promise((resolve,reject) =>{
if(t%2==0){
resolve(t)
}else{
reject(t)
}
})
}
// Promise.all
Promise.all([time11(101), time11(1210)])
.then(result => console.log('success',result))
.catch(error=> console.log('error',error))
I expect the output is success 1210 and error 101, but the actual output is error 101.

Promise#allSettled
The Promise.allSettled() method returns a promise that resolves after all of the given promises have either resolved or rejected, with an array of objects that each describes the outcome of each promise.
const time11=(t) =>{
return new Promise((resolve,reject) =>{
if(t%2==0){
resolve(t)
}else{
reject(t)
}
})
}
Promise
.allSettled([time11(101), time11(1210)])
.then(result =>console.log(result));
However, only supported in a few browsers at the moment. Above snippet will only work with latest version of chrome.
Output, will look something like so:
[
{
"status": "rejected",
"reason": 101
},
{
"status": "fulfilled",
"value": 1210
}
]
Here is to separate the errors from the successes.
const time11=(t) =>{
return new Promise((resolve,reject) =>{
if(t%2==0){
resolve(t)
}else{
reject(t)
}
})
}
Promise
.allSettled([time11(101), time11(1210)])
.then(result =>{
const [success, errors] = result.reduce(([success, errors],cur)=>{
if(cur.status === 'fulfilled'){
success.push(cur);
} else {
errors.push(cur);
}
return [success, errors];
}, [[],[]]);
console.log('errors', errors);
console.log('success', success);
});

Promise.all is all or nothing. It resolves once all promises in the array resolve, or reject as soon as one of them rejects. In other words, it either resolves with an array of all resolved values, or rejects with a single error.
Some libraries have something called Promise.when, which I understand would instead wait for all promises in the array to either resolve or reject, but I'm not familiar with it, and it's not in ES6.
In this case, you can resolve all cases, but you must manage different based in the response at the then chain.
const time11=(t) =>{
return new Promise((resolve,reject) =>{
if(t%2==0){
resolve(true) // Changed to boolean
}else{
resolve(false) // Resolved and Boolean
}
})
}
// Promise.all
Promise.all([time11(101), time11(1210)])
.then(isEven => console.log('isEven',isEven))

Related

Promises with ES6 destructuring fails when one of the promises don't succeed

There are a tons of topics about it but I couldn't find this case. Let me explain:
const [call1, call2, call3] = await Promise.all([promise1, promise2, promise3]);
If they succeed it should return allright, yes? But what if it fails? It throws me this: Unhandled Rejection (TypeError): (intermediate value) is not iterable
I could make it const calls = await.Promise.all(promises) then use it like calls[0] but is it possible any other way?
Awaiting a promise that will reject will cause the function to throw an exception. You handle that with a try-catch.
try {
const [call1, call2, call3] = await Promise.all([promise1, promise2, promise3]);
} catch (error) {
// Assuming the rejection value is an Error instance.
const message = error.message
}
You have to understand how Promise.all works.
From the docs:
The Promise.all() method takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises. This returned promise will resolve when all of the input's promises have resolved, or if the input iterable contains no promises. It rejects immediately upon any of the input promises rejecting or non-promises throwing an error, and will reject with this first rejection message / error.
Example:
const promise1 = Promise.resolve(1)
const promise2 = Promise.reject('dummy error')
const promise3 = Promise.resolve(3)
const promises = [promise1, promise2, promise3]
Promise.all(promises)
.then(values => ...)
.catch(err => console.log(err))
Output: 'dummy error'
Instead, you might consider Promise.allSettled.
From the docs:
The Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
The same example, but with Promise.allSettled:
Promise.allSettled(promises)
.then(values => console.log(values))
Output: [
{ status: "fulfilled", value: 1 },
{ status: "rejected", value: 'dummy error' },
{ { status: "fulfilled", value: 3 }
]
You could have a look at Promise.allSettled(),
this returns a promise that resolves after all of the given promises
have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
So even if any of the promises reject, allSettled will resolve and will give you details of each promise outcome.
Browser compatibility is here:
allSettled compatibility
async function testAllSettled() {
const promises = [1,2,3].map(n => new Promise((res,rej) => setTimeout(n % 2 ? res: rej, 100, n)));
let result = await Promise.allSettled(promises);
console.log('Promise.allSettled result:', result);
}
testAllSettled()
.as-console-wrapper { max-height: 100% !important; top: 0; }

Push promise functions to array. use Promise.all. Promises get resolved to early

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"
// ]

Google Cloud Functions - Retry

I have this idempotent function with multiple promises that I wrote for Google Cloud Functions.
I want to have retry enabled since my used API is pretty inconsistent. This requires a rejected promise to be returned when a retry is needed.
Therefore I tried to return a promise.all([]) but that does not terminate/stop the function when one of the promises fails. It then even proceeds to the promise.all().then()? This should only happen when all 4 promises are successful.
Who can point me in the right direction? Does it even make sense what I'm trying?
exports.scheduleTask = functions
.firestore.document("tasks_schedule/{servicebonnummer}")
.onCreate((snap, context) => {
servicebonnummer = snap.data().data.servicebonnummer;
bondatum = snap.data().data.bondatum;
servicestatus = snap.data().data.servicestatus;
tijdstip = snap.data().data.tijdstip;
firestorePromise = null;
firestoreFinish = null;
cashPromise = null;
cashFinish = null;
//Firebase
//firestoreSchedule executes the required promise
//checkFinished points to a database where it checks a boolean for idempotency
//firestoreFinish writes to this database and sets the boolean to true when the promise is successful
if (!checkFinished("tasks_schedule", servicebonnummer, "firestore")) {
firestorePromise = scheduleFirestore(
servicebonnummer,
bondatum,
servicestatus,
tijdstip
)
.then(output => {
firestoreFinish = markFinished(
"tasks_schedule",
servicebonnummer,
"firestore"
);
return output;
})
.catch(error => {
console.error(
"scheduleFirestore - Error connecting to Firestore: ",
error
);
return error;
});
}
//SOAP API
//cashSchedule executes the required promise
//checkFinished points to a database where it checks a boolean for idempotency
//cashFinish writes to this database and sets the boolean to true when the promise is successful
if (!checkFinished("tasks_schedule", servicebonnummer, "cash")) {
cashPromise = scheduleCash(
servicebonnummer,
moment(bondatum),
servicestatus,
tijdstip
)
.then(result => {
if (result[0].response.code === "2") {
cashFinish = markFinished(
"tasks_schedule",
servicebonnummer,
"cash"
);
return result;
}
throw new Error("Validation error, response not successful");
})
.catch(error => {
console.error("scheduleCash - Error connecting to CASH API: ", error);
return error;
});
}
//CHECK PROMISES
return Promise.all([
firestorePromise,
firestoreFinish,
cashPromise,
cashFinish
])
.then(result => {
removeTask("tasks_schedule", servicebonnummer);
return result;
})
.catch(error => {
console.error("scheduleTask - Retry: ", error);
return error;
});
});
If you code:
let somePromise = new Promise(...);
return somePromise.then(funcA).catch(funcB);
Then you are indeed returning a promise. However, since you have handlers for that promise in your code, we need to look at what happens in more detail. Let us assume that somePromise is rejected. This will mean that the catch() handler will be invoked. It is the outcome of that catch handler that will be the ultimate resolution of the returned promise.
If we look at the MDN docs for Promise.catch() we find the following:
The Promise returned by catch() is rejected if onRejected throws an
error or returns a Promise which is itself rejected; otherwise, it is
resolved.
If we look at your code,
catch(error => {
console.error("scheduleTask - Retry: ", error);
return error;
});
And now ask:
Does this code throw an error? Nope ... it has no throw statement in it and hence just returns the values passed in.
Does the code return a Promise? Nope ... it is passed an error value and simply returns that error value which I am pretty sure will not itself be a Promise.
This means that the overall Promise returned is concluded in a resolved state and not a rejected state and hence the overall Cloud Function is considered to have concluded and is not retried.
Your options may be:
catch(error => {
console.error("scheduleTask - Retry: ", error);
throw error;
});
or
catch(error => {
console.error("scheduleTask - Retry: ", error);
return Promise.reject(error);
});
References:
Promise.prototype.catch()
Promise.reject()

How to iterate an array in javascript, with promises inside the loop block and wait for all promises to be completed to continue

I need to iterate an array in javascript with some values that will be used to call an asynchronous function that returns a promise. I can´t continue the next code section without all promises were completed.
In the following example, the function "processInvoices" has to resolve a promise until all promises inside were completed (assume that "confirmInvoice" is an asynchronous function that has different response times):
processInvoices(invoices)
{
return new promise((resolve,reject)=>
{
invoices.forEach(number=>
{
confirmInvoice(number)
.then(result=>{
if (!result)
{reject('Error confirming the invoice');}
});
});
resolve(true); // Resolving here doesn´t mean that all promises were completed!
});
}
init() // triggered at load..
{
let invoices = [2,4,8,16,31];
processInvoices(invoices)
.then(result=>
{
if (result) // It´s probable that the following message isn´t accurate:
console.log('All invoices were processed');
}).catch(reason=>{console.error(reason)});
}
With the code above, I can´t be sure that the "console.log (or any routine)" will be executed right away after all promises were completed.
UPDATE Promise.all(iterable) solves the problem :
processInvoices(invoices)
{
return new promise((resolve,reject)=>
{
var promisesArray = []; // store all promises here
invoices.forEach(number=>
{
promisesArray.push(confirmInvoice(number));
});
Promise.all(promisesArray).then (results=>{
// validate all results and reject if necessary...
if (validateResults(results)) {
// if all ok
resolve('All invoices were processed successfully');
}
else {
reject('Error processing one or more invoices'); // just for demo purposes
}
});
});
}
init() // triggered at load..
{
let invoices = [2,4,8,16,31];
processInvoices(invoices)
.then(result=>
{
console.log(result);
}).catch(reason=>{console.error(reason)});
}
forEach runs synchronously. If you want to wait for all Promises to resolve before the full processInvoices resolves, you should use Promise.all instead; map each invoice number to a Promise and call Promise.all on the resulting array of Promises. Also, your
if (!result) {resolve(false);}
sounds like it's an attempt to handle an error, in case there is no result - in this case, you should reject the Promise instead of calling resolve. Ideally, the failed confirmInvoice call would result in a rejected Promise, but if that's not something you can fix, throw an error if result is falsey so you can handle it in a catch in init. For example:
function processInvoices(invoices) {
return Promise.all(
invoices.map(number => (
confirmInvoice(number)
.then(result => {
// This will result in the `Promise.all` rejecting:
if (!result) throw new Error('Failed to confirm invoice ' + number);
})
))
);
}
function init() {
const invoices = [2, 4, 8, 16, 31];
processInvoices(invoices)
.then(result => {
console.log('All invoices were processed');
})
.catch((err) => {
console.log('err: ' + err);
});
}

Why is my asynchronous function returning Promise { <pending> } instead of a value?

My code:
let AuthUser = data => {
return google.login(data.username, data.password).then(token => { return token } )
}
And when i try to run something like this:
let userToken = AuthUser(data)
console.log(userToken)
I'm getting:
Promise { <pending> }
But why?
My main goal is to get token from google.login(data.username, data.password) which returns a promise, into a variable. And only then preform some actions.
The promise will always log pending as long as its results are not resolved yet. You must call .then on the promise to capture the results regardless of the promise state (resolved or still pending):
let AuthUser = function(data) {
return google.login(data.username, data.password).then(token => { return token } )
}
let userToken = AuthUser(data)
console.log(userToken) // Promise { <pending> }
userToken.then(function(result) {
console.log(result) // "Some User token"
})
Why is that?
Promises are forward direction only; You can only resolve them once. The resolved value of a Promise is passed to its .then or .catch methods.
Details
According to the Promises/A+ spec:
The promise resolution procedure is an abstract operation taking as
input a promise and a value, which we denote as [[Resolve]](promise,
x). If x is a thenable, it attempts to make promise adopt the state of
x, under the assumption that x behaves at least somewhat like a
promise. Otherwise, it fulfills promise with the value x.
This treatment of thenables allows promise implementations to
interoperate, as long as they expose a Promises/A+-compliant then
method. It also allows Promises/A+ implementations to “assimilate”
nonconformant implementations with reasonable then methods.
This spec is a little hard to parse, so let's break it down. The rule is:
If the function in the .then handler returns a value, then the Promise resolves with that value. If the handler returns another Promise, then the original Promise resolves with the resolved value of the chained Promise. The next .then handler will always contain the resolved value of the chained promise returned in the preceding .then.
The way it actually works is described below in more detail:
1. The return of the .then function will be the resolved value of the promise.
function initPromise() {
return new Promise(function(res, rej) {
res("initResolve");
})
}
initPromise()
.then(function(result) {
console.log(result); // "initResolve"
return "normalReturn";
})
.then(function(result) {
console.log(result); // "normalReturn"
});
2. If the .then function returns a Promise, then the resolved value of that chained promise is passed to the following .then.
function initPromise() {
return new Promise(function(res, rej) {
res("initResolve");
})
}
initPromise()
.then(function(result) {
console.log(result); // "initResolve"
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("secondPromise");
}, 1000)
})
})
.then(function(result) {
console.log(result); // "secondPromise"
});
I know this question was asked 2 years ago, but I run into the same issue and the answer for the problem is since ES2017, that you can simply await the functions return value (as of now, only works in async functions), like:
let AuthUser = function(data) {
return google.login(data.username, data.password)
}
let userToken = await AuthUser(data)
console.log(userToken) // your data
The then method returns a pending promise which can be resolved asynchronously by the return value of a result handler registered in the call to then, or rejected by throwing an error inside the handler called.
So calling AuthUser will not suddenly log the user in synchronously, but returns a promise whose then registered handlers will be called after the login succeeds ( or fails). I would suggest triggering all login processing by a then clause of the login promise. E.G. using named functions to highlight the sequence of flow:
let AuthUser = data => { // just the login promise
return google.login(data.username, data.password);
};
AuthUser(data).then( processLogin).catch(loginFail);
function processLogin( token) {
// do logged in stuff:
// enable, initiate, or do things after login
}
function loginFail( err) {
console.log("login failed: " + err);
}
If that situation happens for a multiple values like an array.
[
Promise { <pending> },
Promise { <pending> },
Promise { <pending> },
Promise { <pending> },
Promise { <pending> }
]
You can use Promise.all() this will resolve all promises.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
See the MDN section on Promises. In particular, look at the return type of then().
To log in, the user-agent has to submit a request to the server and wait to receive a response. Since making your application totally stop execution during a request round-trip usually makes for a bad user experience, practically every JS function that logs you in (or performs any other form of server interaction) will use a Promise, or something very much like it, to deliver results asynchronously.
Now, also notice that return statements are always evaluated in the context of the function they appear in. So when you wrote:
let AuthUser = data => {
return google
.login(data.username, data.password)
.then( token => {
return token;
});
};
the statement return token; meant that the anonymous function being passed into then() should return the token, not that the AuthUser function should. What AuthUser returns is the result of calling google.login(username, password).then(callback);, which happens to be a Promise.
Ultimately your callback token => { return token; } does nothing; instead, your input to then() needs to be a function that actually handles the token in some way.
Your Promise is pending, complete it by
userToken.then(function(result){
console.log(result)
})
after your remaining code.
All this code does is that .then() completes your promise & captures the end result in result variable & print result in console.
Keep in mind, you cannot store the result in global variable.
Hope that explanation might help you.
I had the same issue earlier, but my situation was a bit different in the front-end. I'll share my scenario anyway, maybe someone might find it useful.
I had an api call to /api/user/register in the frontend with email, password and username as request body. On submitting the form(register form), a handler function is called which initiates the fetch call to /api/user/register. I used the event.preventDefault() in the beginning line of this handler function, all other lines,like forming the request body as well the fetch call was written after the event.preventDefault(). This returned a pending promise.
But when I put the request body formation code above the event.preventDefault(), it returned the real promise. Like this:
event.preventDefault();
const data = {
'email': email,
'password': password
}
fetch(...)
...
instead of :
const data = {
'email': email,
'password': password
}
event.preventDefault();
fetch(...)
...
Try this
var number1 = document.getElementById("number1");
var number2 = document.getElementById("number2");
startAsync.addEventListener("click", function() {
if (number1.value > 0 && number2.value > 0) {
asyncTest(parseInt(number1.value), parseInt(number2.value)).then(function(result) {
document.getElementById("promiseResolved").textContent = "promiseResolved: " + result
});
} else {
asyncTest(1, 2).then(function(result) {
document.getElementById("promiseResolved").textContent = "promiseResolved: " + result
});
}
});
async function asyncTest(a, b) {
return await (a + b);
};
<button id="startAsync">start Async function</button><br />
<input type="number" id="number1" /><br />
<input type="number" id="number2" /><br />
<span id="promiseResolved"></span><br />
Im my case (JS) I forgot to add await

Categories