const a = [1, 2, 3, 4, 5];
const f = () => new Promise((resolve, reject) => resolve(4));
const g = () => {
Promise.all(a.map((member) => f().then((res) => res)))
.then((result) => {
console.log(result)
});
}
g();
Why do I not need another then attached to {return res;} here?
I read that when you have a return (something)inside a then, another then must be attached, but its not the case here. Help?
Promise.all expects an array of promises. .then returns a promise. Therefore your mapping logic converts an array of numbers to an array of promises, exactly what you need.
.then((res) => {return res;}) is completely unnecessary btw, return f(); would suffice. You can even simplify your current code further to:
Promise.all(a.map(f)).then(result => console.log(result));
I read that when you have a return (something) inside a then, another then must be attached
This has nothing to do with .then. .then simply returns a promise. To access the result of a promise you need to attach a handler via .then.
You don't need to do this here because you are passing the promises to Promise.all. You are accessing that result via .then((result)=>{console.log(result)}).
Why do I not need another then attached to {return res;} here?
I read that when you have a return (something)inside a then, another
then must be attached, but its not the case here. Help?
There is another .then() attached to Promise.all(). Did you mean .catch() should be attached to avoid Uncaught (in promise)?
Note also, Promise.all() is not returned from g() call, to further chain the Promise.
.then() and .catch() chained within .map() callback can be utilized to either handle error or rejected Promise and return a resolved Promise to .then() chained to Promise.all(); or to throw current or new Error() to Promise.all() call.
The pattern can also be used to return all promises passed to .map(), whether resolved or rejected to .then(), without immediately calling .catch() chained to Promise.all().
function f (index) {
return new Promise(function (resolve, reject) {
if (index !== 4) resolve(4);
else reject("err at index " + index)
})
}
var a =[1, 2, 3, 4, 5];
function g () {
return Promise.all(a.map((member, index)=>{
return f(index).then((res) => {return res;})
.catch(e => {console.log(e); throw new Error(e)})
}))
.then((result)=>{console.log(result); return result})
.catch(e => {
// handle error here, return resolved `Promise`,
// or `throw new Error(e)` to propagate error to
// `.catch()` chained to `g()` call
console.log("handle error within g", e);
return "error " + e.message + " handled"});
}
g()
.then(data => {console.log(data) /* `error err at 4 handled` */ })
.catch(e => console.log("done", e));
Related
I do realise that when returning a non-promise in a .then() handler, it is immediately passed on to the next handler, if it's a promise that is returned, executing is halted for the promise to resolve before it is passed on to the next handler.
Also I know that only one value can be returned from a promise.
That beeing said, how would I go about returning multiple parameters from one .then() handler to the next one? Esepcailly if it's a mix of promises and non-promises. Currently I put everything into a custom object, return it, and use async await in the following then() handler for the promises to reslolve.
Then is use the resolved promise values and the non-promise value to do some work together.
This works fine but my gut is saying that this is somehow not the way it is supposed to be... maybe?
Example:
const current = 'blah';
const previous = 'blubb';
this.doSomeAsyncWork()
.then(
result => {
const nonPromiseValue = new domSomethingSynchronous(current, previous);
// "custom object, mix of promises and non-promises"
return {
nonPromise: nonPromise,
promiseA: ControllerA.asyncOperationA(current, nonPromiseValue.someProperty),
promiseB: ControllerB.asyncOperationB(nonPromiseValue.someOtherProperty),
}
}
)
.then(
async x => {
const nonPromiseValue = x.nonPromiseValue;
const valueA = await x.promiseA;
const valueB = await x.promiseB;
// do something with the results of those three variables
}
)
.catch(
// ...
)
Use return Promise.all on the array of Promises and non-Promises at the end of a .then, and then you can destructure the results immediately in the next .then, no await nor async needed.
The Promise.all will resolve once all Promises in the array have resolved. The non-Promises passed to it will just be passed to the next .then.
const makeProm = () => new Promise(resolve => setTimeout(resolve, 1000, 'resolveValue'));
Promise.resolve()
.then(() => {
const prom = makeProm();
const otherValue = 'foo';
return Promise.all([prom, otherValue]);
})
.then(([resolveValue, otherValue]) => {
console.log(resolveValue, otherValue);
});
I do realise that when returning a non-promise in a .then() handler, it is immediately passed on to the next handler, if it's a promise that is returned, executing is halted for the promise to resolve before it is passed on to the next handler.
Also I know that only one value can be returned from a promise.
That beeing said, how would I go about returning multiple parameters from one .then() handler to the next one? Esepcailly if it's a mix of promises and non-promises. Currently I put everything into a custom object, return it, and use async await in the following then() handler for the promises to reslolve.
Then is use the resolved promise values and the non-promise value to do some work together.
This works fine but my gut is saying that this is somehow not the way it is supposed to be... maybe?
Example:
const current = 'blah';
const previous = 'blubb';
this.doSomeAsyncWork()
.then(
result => {
const nonPromiseValue = new domSomethingSynchronous(current, previous);
// "custom object, mix of promises and non-promises"
return {
nonPromise: nonPromise,
promiseA: ControllerA.asyncOperationA(current, nonPromiseValue.someProperty),
promiseB: ControllerB.asyncOperationB(nonPromiseValue.someOtherProperty),
}
}
)
.then(
async x => {
const nonPromiseValue = x.nonPromiseValue;
const valueA = await x.promiseA;
const valueB = await x.promiseB;
// do something with the results of those three variables
}
)
.catch(
// ...
)
Use return Promise.all on the array of Promises and non-Promises at the end of a .then, and then you can destructure the results immediately in the next .then, no await nor async needed.
The Promise.all will resolve once all Promises in the array have resolved. The non-Promises passed to it will just be passed to the next .then.
const makeProm = () => new Promise(resolve => setTimeout(resolve, 1000, 'resolveValue'));
Promise.resolve()
.then(() => {
const prom = makeProm();
const otherValue = 'foo';
return Promise.all([prom, otherValue]);
})
.then(([resolveValue, otherValue]) => {
console.log(resolveValue, otherValue);
});
I am trying to understand how promises work by creating the Promise.all method from an exercise of this book:
https://eloquentjavascript.net/11_async.html#i_Ug+Dv9Mmsw
I tried looping through the whole array that's given as argument to the method itself, using .then for successful promises where the body of the handler pushes the result to a binding i defined previously outside of the loop, for rejected promises, i used .catch in a way that it takes the rejected value as a "reason" and rejects the main promise giving it an error
function Promise_all(promises) {
return new Promise((resolve, reject) => {
if(promises.length == 0) resolve(promises);
let fullArray = [];
for(let i=0; i<promises.length ; i++){
promises[i]
.then(x => fullArray.push(x))
.catch(reason => reject(new Error(reason)));
}
resolve(fullArray);
});
}
What i expected the function to do the following:
-Pick a promise from the "Promises" array.
-Solve the promise (if successful) by using the .then method on it with a handler function that just pushes the result to "fullArray".
-Solve the promise (if rejected) by using the .catch method on it with a handler function that simply calls the reject handler of the main promise that will be returned by "Promise_all".
-When the loop finishes, simply resolve the promise the "fullArray" of successful promises.
The code simply doesn't work the way i thought it would, using the test code of the book doesn't return the expected results:
Promise_all([]).then(array => {
console.log("This should be []:", array);
});
function soon(val) {
return new Promise(resolve => {
setTimeout(() => resolve(val), Math.random() * 500);
});
}
Promise_all([soon(1), soon(2), soon(3)]).then(array => {
console.log("This should be [1, 2, 3]:", array);
});
Promise_all([soon(1), Promise.reject("X"), soon(3)])
.then(array => {
console.log("We should not get here");
})
.catch(error => {
if (error != "X") {
console.log("Unexpected failure:", error);
}
});
The issue with the code, as #Bergi said, was that the ".then" and ".catch" callbacks are called asynchronously and as a consequence of this, "fullArray" wasn't even filled when "resolve()" was called even when it was outside of the loop. To solve this, i simply added "resolve()" inside of the promise that finishes last, to do this, i simply added a "counter" binding that has the value of the length of the "promises" array, and is reduced by 1 every time ".then" was called on a promise and when this "counter" equals 0, it calls "resolve()".
But that only solves the problem of filling the "fullArray" with the promises, not the issue of those promises being ordered correctly by the order they were called, to solve this i simply enumerated them in the array with the "i" binding of the loop, the end result was this:
function Promise_all(promises) {
return new Promise((resolve, reject) => {
if(promises.length == 0) resolve(promises);
let fullArray = [],
counter = promises.length;
for(let i=0; i< promises.length ; i++){
promises[i]
.then(x => {
fullArray[i] = x;
counter--;
if(counter == 0) resolve(fullArray)})
.catch(reason => reject(new Error(reason)));
}
});
}
I do realise that when returning a non-promise in a .then() handler, it is immediately passed on to the next handler, if it's a promise that is returned, executing is halted for the promise to resolve before it is passed on to the next handler.
Also I know that only one value can be returned from a promise.
That beeing said, how would I go about returning multiple parameters from one .then() handler to the next one? Esepcailly if it's a mix of promises and non-promises. Currently I put everything into a custom object, return it, and use async await in the following then() handler for the promises to reslolve.
Then is use the resolved promise values and the non-promise value to do some work together.
This works fine but my gut is saying that this is somehow not the way it is supposed to be... maybe?
Example:
const current = 'blah';
const previous = 'blubb';
this.doSomeAsyncWork()
.then(
result => {
const nonPromiseValue = new domSomethingSynchronous(current, previous);
// "custom object, mix of promises and non-promises"
return {
nonPromise: nonPromise,
promiseA: ControllerA.asyncOperationA(current, nonPromiseValue.someProperty),
promiseB: ControllerB.asyncOperationB(nonPromiseValue.someOtherProperty),
}
}
)
.then(
async x => {
const nonPromiseValue = x.nonPromiseValue;
const valueA = await x.promiseA;
const valueB = await x.promiseB;
// do something with the results of those three variables
}
)
.catch(
// ...
)
Use return Promise.all on the array of Promises and non-Promises at the end of a .then, and then you can destructure the results immediately in the next .then, no await nor async needed.
The Promise.all will resolve once all Promises in the array have resolved. The non-Promises passed to it will just be passed to the next .then.
const makeProm = () => new Promise(resolve => setTimeout(resolve, 1000, 'resolveValue'));
Promise.resolve()
.then(() => {
const prom = makeProm();
const otherValue = 'foo';
return Promise.all([prom, otherValue]);
})
.then(([resolveValue, otherValue]) => {
console.log(resolveValue, otherValue);
});
New at promises, so feel free to be verbose.
I am writing a function "extra_promises_at_start_and_end" that returns a promise to do something.
This function may know immediately that it will fail (ie: return a promise that is rejected). Question 1: Is there something like Promise.give_me_a_rejected_promise(..) or do I have to create a promise and reject it just like my code does?
Similarly, my function "extra_promises_at_start_and_end" calls other functions that return promises. At the end of this async chaining of work, I need to some final processing. Question 2a/2b: Since my function returns a promise, I need to create a promise to do this last bit of work. Is this correct that I need to create a promise and immediately accepted or reject it? Is there a Promise.give_me_a_rejected_promise(..).
My code works as expected, just feels like I am missing something, and so generating redundant code.
Code in question:
// this is the function that may have redundant code
// see question 1 and 2
function extra_promises_at_start_and_end() {
// fake out some module scope variable that indicates if this call is allowed to proceed or not
let ok_to_proceed = Math.random() > 0.5
// this function "extra_promises_at_start_and_end returns" a promise,
// Question 1: I need to create a Promise just to reject it immediatly?
if (!ok_to_proceed) {
return new Promise((resolve, reject) => { reject("failed before starting anything") }) // feels wrong
}
// do 5 things in sequence
return another_module_promise_to_do_something(1).then(() => {
return another_module_promise_to_do_something(2)
}).then(() => {
return another_module_promise_to_do_something(3)
}).then(() => {
return another_module_promise_to_do_something(4)
}).then(() => {
return another_module_promise_to_do_something(5)
}).then(() => {
// need to do something after the above 5 tasks are done,
console.log("doing something after all 5 things are done")
// this function "extra_promises_at_start_and_end" returns a promise,
// Question 2a: I need to create a promise just to resolve it immediatly?
return new Promise((resolve, reject) => { resolve(); }) // feels wrong
}).catch((id) => {
// this function extra_promises_at_start_and_end returns a promise,
// Question 2b: I need to create one just to reject it immediatly?
return new Promise((resolve, reject) => { reject(id); }) // feels wrong
})
}
The caller of this code is expecting a promise.
// run the test
console.log("calling something that will return a promise to let me know when it's done");
extra_promises_at_start_and_end()
.then(() => {
console.log("done :)")
}).catch((id) => { console.log("failed id = " + id) })
Finally, a stub for testing my function
// pretend this is a complex task (ie: not suitable for inlining)
// done by some other module
// it returns a promise
function another_module_promise_to_do_something(id) {
console.log("starting " + id)
let P = new Promise((resolve, reject) => {
console.log(" inside promise " + id)
setTimeout(() => {
if (Math.random() > 0.1) {
console.log(" finished " + id);
resolve();
} else {
console.log(" failed " + id)
reject(id);
}
}, Math.random() * 1000)
})
return P;
}
If this is the way it is supposed to be done, then let me know and I will stop searching for the correct way to use promises.
What I learned is:
Promise.resolve(value) method returns a Promise object that is resolved with the given value. So anyone calling my function can do a then on the response.
Promise.reject(reason) method returns a Promise object that is rejected with the given reason. So any chaining will fail as needed.
Any return value in then will get encapsulated in a promise. Feels like this obscures the intent. So not using.
My new function is as follows:
function promise_something() {
// fake out some module scope variable that indicates if this call is allowed to proceed or not
let ok_to_proceed = Math.random() > 0.5
if (!ok_to_proceed) {
return Promise.reject("failed before starting anything")
}
// do 5 things in sequence
return another_module_promise_to_do_something(1).then(() => {
return another_module_promise_to_do_something(2)
}).then(() => {
return another_module_promise_to_do_something(3)
}).then(() => {
return another_module_promise_to_do_something(4)
}).then(() => {
return another_module_promise_to_do_something(5)
}).then(() => {
// need to do something after the above 5 tasks are done,
console.log("doing something after all 5 things are done")
return Promise.resolve()
}
The then() and catch() functions of a Promise return a Promise.
Your last big chunk of code is indeed redundant. If you needed to do any processing after the 5th chained invocation of then(), you could just chain another then().
Here's a more bare-bones version of your code to illustrate:
const countFromOneToFive = () => {
if (Math.random() > 0.5) {
return Promise.reject("Cosmic radiation ruined your promises. Great.");
}
return Promise.resolve([])
.then((countToFive) => {
countToFive.push(1);
return countToFive;
})
.then((countToFive) => {
countToFive.push(2);
return countToFive;
})
.then((countToFive) => {
countToFive.push(3);
return countToFive;
})
.then((countToFive) => {
countToFive.push(4);
return countToFive;
})
.then((countToFive) => {
countToFive.push(5);
return countToFive;
});
};
countFromOneToFive()
.then((countToFive) => {
countToFive.forEach((number) => console.log(number));
})
.catch((error) => {
console.log(error, "Curses!");
});
You can use Promise.reject() to just return a rejected Promise. This is handled in the catch statement in the bottom.
You can do all of your processing with as many then() invocations as you want. You can simply return after your final then() to have a Promise. From there, treat it like any promise and append then()s and catch()s as you see fit.
1: Is there something like Promise.give_me_a_rejected_promise(..) or do I have to create a promise and reject it just like my code does?
like Promise.reject(err)?
// Question 2a: I need to create a promise just to resolve it immediatly?
The point where this is written in your code is inside a promise chain. You don't need to do anything at all (in that context), to return a resolved Promise, except maybe not throwing.
This Promise will resolve to any value you return, except undefined. And if you return undefined (explicitely or implicitely), this Promise will resolve to the last value before that, in the Promise chain.
Only if you need to explicitely resolve to undefined you'd have to return Promise.resolve().
// Question 2b: I need to create one just to reject it immediatly?
Still inside the Promise chain: Since a rejected value is like a thrown Error in sync code, all you need to do here is to throw.
But that is pointless in the context you asked. Why catching an Error, just to immediately throw the very same Error, without doing anything else in that catch-block.
So your code could be like
function extra_promises_at_start_and_end() {
// fake out some module scope variable that indicates if this call is allowed to proceed or not
let ok_to_proceed = Math.random() > 0.5
if (!ok_to_proceed) {
return Promise.reject("failed before starting anything");
}
// do 5 things in sequence
return another_module_promise_to_do_something(1)
.then(() => another_module_promise_to_do_something(2))
.then(() => another_module_promise_to_do_something(3))
.then(() => another_module_promise_to_do_something(4))
.then(() => another_module_promise_to_do_something(5))
.then(() => {
// need to do something after the above 5 tasks are done,
console.log("doing something after all 5 things are done")
return null; //so the previous value is no longer propagated by this Promise
})
//.catch(id => { throw id }) //is pointless.
}
When you say "This Promise will resolve to any value you return". So returning new Promise((resolve, reject) => { resolve 100}).then .... blaw blaw blaw is the same as 100.then .. blaw blaw blaw ?
Not exactly. Inside a Promise chain, returning a plain value like 100 or a Promise that resolves to 100 (like return Promise.resolve(100)) is equivalent in result ...
var foo1 = somePromise.then(() => {
//do something
return 100
})
//is equivalent in result to
var foo2 = somePromise.then(() => {
//do something
return Promise.resolve(100);
})
//or to
var foo3 = somePromise.then(() => {
//do something
return new Promise(resolve => resolve(100));
})
foo1, foo2, foo3 are all promises that resolve to the value 100 after somePromise has finished. That's equivalent in result.
... but you can't call then() on a number.
//you can do
somePromise.then(() => {
//do something
return 100
}).then(...)
//or sometimes you want to do
somePromise.then((value) => {
//usually because you need `value` inside `.then(...)`
return somethingAsync().then(...)
})
//but you can NOT do
somePromise.then(() => {
//do something
return 100.then(...)
})