If I have two promises A and B, only one of which will succeed, how can I get whichever one fulfills successfully? I'm looking for something similar to Promise.race, but which will return only the first promise that fulfills. I'm using promises from ES6.
Invert the polarity of the promises, and then you can use Promise.all, because it rejects on the first rejected promise, which after inversion corresponds to the first fulfilled promise:
const invert = p => new Promise((res, rej) => p.then(rej, res));
const firstOf = ps => invert(Promise.all(ps.map(invert)));
// Utility routines used only in testing.
const wait = ms => new Promise(res => setTimeout(() => res(ms), ms));
const fail = f => Promise.reject(f);
const log = p => p.then(v => console.log("pass", v), v => console.log("fail", v));
// Test.
log(firstOf([wait(1000), wait(500) ]));
log(firstOf([wait(1000), fail("f1")]));
log(firstOf([fail("f1"), fail("f2")]));
This will return the value of the first fulfilled promise, or if all reject, an array of rejection reasons.
ES2021 / ES12 - Promise.any
Promise.any - first fulfilled Promise wins.
const promiseA = Promise.reject();
const promiseB = new Promise((resolve) => setTimeout(resolve, 100, 'succeed'));
const promises = [promiseA, promiseB];
Promise.race(promises).then((value) => console.log(value)); // rejected promise
Promise.any(promises).then((value) => console.log(value)); // "succeed"
Notice that any is ignoring the first rejected promise - promiseA because promiseB is being resolved
If all of the given promises are rejected, then the returned promise is rejected.
This is finished proposal and it's scheduled for ES2021 (expected to be released in June 2021)
If you want the first promise that resolves successfully and you want to ignore any rejections that come before that, then you can use something like this:
// returns the result from the first promise that resolves
// or rejects if all the promises reject - then return array of rejected errors
function firstPromiseResolve(array) {
return new Promise(function(resolve, reject) {
if (!array || !array.length) {
return reject(new Error("array passed to firstPromiseResolve() cannot be empty"));
}
var errors = new Array(array.length);
var errorCntr = 0;
array.forEach(function (p, index) {
// when a promise resolves
Promise.resolve(p).then(function(val) {
// only first one to call resolve will actually do anything
resolve(val);
}, function(err) {
errors[index] = err;
++errorCntr;
// if all promises have rejected, then reject
if (errorCntr === array.length) {
reject(errors);
}
});
});
});
}
I don't see how you can use Promise.race() for this because it simply reports the first promise to finish and if that first promise rejects, it will report a rejection. So, it is not doing what you asked in your question which is to report the first promise that resolves (even if some rejections finished before it).
FYI, the Bluebird promise library has both Promise.some() and Promise.any() which can handle this case for you.
I had the same question and gave it a go. You learn a lot by trying these problems yourself!
The accepted answer is very elegant but uses Promise.all which takes the fun for someone learning Promises; also a bit hard to follow imo.
jfriend00's answer is similar to mine but has that logic that goes beyond the Promises fundamentals which is most important here.
I have made use of those nice helper functions present on the accepted answer:
function firstPromise(promiseL, promiseR) {
return new Promise((resolve, reject) => {
promiseL.then(l => {
resolve(l);
}).catch(error => null);
promiseR.then(r => {
resolve(r);
}).catch(error => null);
promiseL.catch(errorL => {
promiseR.catch(errorR => {
reject(errorL + errorR);
})
})
})
}
const wait = ms => new Promise(res => setTimeout(() => res(ms), ms));
const log = p => p.then(v => console.log("pass", v), v => console.log("fail", v));
log(firstPromise(wait(1000), wait(500)));
log(firstPromise(wait(1000), Promise.reject("Bar")));
log(firstPromise( Promise.reject("Foo"), wait(500)));
log(firstPromise( Promise.reject("Foo"), Promise.reject("Bar")));
//example 1
var promise_A = new Promise(function(resolve, reject) {
// выполнить что-то, возможно, асинхронно…
setTimeout(function(){
return resolve(10);
//return reject(new Error('ошибка'))
},10000)
});
var promise_B = new Promise(function(resolve, reject) {
// выполнить что-то, возможно, асинхронно…
setTimeout(function(){
return resolve(100);
},2000)
});
/*
//[100,10]
Promise.all([
promise_A,promise_B
]).then(function(results){
console.log(results)
});
*/
//100
Promise.race([
promise_A,promise_B
]).then(function(results){
console.log(results)
});
Related
I'm doing multiple requests to different APIs (simulated by the delayedPromise function) which each take a certain amount of time to fulfill. If one of them fulfills I return the value it resolves and that's it. I'm currently doing those one after the other since the first request would be the most accurate while the second would be less accurate etc. So if the first request fails and the second fulfills I return the second
To speed things up I thought about parallelizing those requests. To accomplish that I'm creating an array that holds the promises and it awaits the result of the first element and if that fails it awaits the result of the second element etc...
Here's the code:
function promiseTest() {
return new Promise(function (resolve, reject) {
let promises = []
new Promise(async function (resolve, reject) {
promises.push(delayedPromise(true, 10000, "1")) //If true resolves after 10000 ms; false = reject
promises.push(delayedPromise(false, 1000, "2"))
promises.push(delayedPromise(false, 1000, "3"))
let answer;
let answer2
let answer3;
answer = await promises[0].catch(async err => {
answer2 = await promises[1].catch(async err => {
answer3 = await promises[2].catch(err => { reject(err) })
})
})
if (answer != undefined) {
resolve(answer)
}
else if (answer2 != undefined) {
resolve(answer2)
}
else if (answer3 != undefined) {
resolve(answer3)
}
}).then(res => resolve(res)).catch(err => {reject(err)})
})
}
function delayedPromise(bool, time,string ="") {
return new Promise(function (resolve, reject) {
if (bool) {
setTimeout(resolve(string), time)
}
else {
setTimeout(reject(), time)
}
})
}
The issue is that while this approach works it's
A: Most likely not the best solution and hardly scalable
and B: The exact code above is creating a bunch of uncaught exceptions errors. I believe this is when I receive a result for answer1 I'm not handling any of the rejections the other functions returned.
Is there any better way to do these kinds of things/ fix the above mentioned concerns?
I'd appreciate any ideas or improvements!
Many thanks in advance
PS: If you can phrase my title better please let me know I'm kind of struggling to convey my issue in one sentence
There is no builtin for this. Promise.any and Promise.allSettled are quite similar but do not do exactly what you want. You'll have to build it yourself indeed. This is what I would do:
async function first(iterable) {
const promises = [];
for (const thenable of iterable) {
const promise = Promise.resolve(thenable);
promises.push(promise);
promise.catch(() => {}); // ignore rejections, we'll handle them later
}
const errors = [];
for (const promise of promises) {
try {
return await promise;
} catch(error) {
errors.push(error);
}
}
throw new AggregateError(errors);
}
Code in the question simplifies to:
function promiseTest() {
let promises = [
delayedPromise(true, 10000, '1'),
delayedPromise(false, 1000, '2'),
delayedPromise(false, 1000, '3')
];
return promises[0]
.catch(() => promises[1])
.catch(() => promises[2]);
}
As soon as a successful promise is encountered, the chain will stop catching and the successful result will drop all the way through to be delivered (promise-wrpped) to promiseTest's caller.
The catch-chain could also be written as follows ...
Promise.reject()
.catch(() => promises[0])
.catch(() => promises[1])
.catch(() => promises[2]);
... which helps in formulating a proceduralized version for an array of any length, as follows:
function promiseTest() {
let promises = [...]; // array of Promises (any length)
return promises.reduce((p_, p) => p_.catch(() => p), Promise.reject());
}
Assuming that measures to avoid unhandled rejections are not taken in the formulation of the promises array, you can do so (with a series of "branch-catches") in the reduction ...
function promiseTest() {
let promises = [...]; // array of Promises (any length).
return promises.reduce((p_, p) => {
p.catch(() => null); // avoid unhandled rejections.
return p_.catch(() => p);
}, Promise.reject());
}
I think weighting the result to favor promises in order while still running them all in parallel (for best end-to-end time) will require building a special loop. I think you can do it like this:
const errResult = Symbol('errResult');
// fns is an array of functions to call
// args is a array of arguments to pass to each function (same args to each)
async function getResult(fns, args) {
// start all the async operations to run in parallel
const promises = fns.map(fn => {
// Rejections will resolve with a sentinel value so we can detect them
// This helps us avoid ever getting an unhandled rejection error
// on promises that reject after we've already found our desired value
return fn(...args).catch(err => {
return errResult;
});
});
// now await them in order so you can find the first
// one that completes (in order)
// Promises that rejected have been converted into an errResult
// resolve to help in avoiding unhandled rejections
// after we return with our good first result
for (let p of promises) {
let result = await p;
if (result !== errResult) {
return result;
}
}
throw new Error('no results');
}
// sample usage:
getResult([fn1, fn2, fn3, fn4], [argA,argB]).then(result => {
console.log(result);
}).catch(err => {
console.log("no results found");
});
If you want different args to each function, then just build that into fn1, fn2, etc... so they call the root function with their desired arguments and then let the args array be an empty array.
in one of youtube tutorial videos about promises I found following code:
let cleanRoom = function() {
return new Promise((resolve, reject) => {
resolve();
});
};
let removeGarbage = function() {
return new Promise((resolve, reject) => {
resolve();
});
};
let winIcecream = function() {
return new Promise((resolve, reject) => {
resolve();
});
};
cleanRoom().then(function(){
return removeGarbage();
}).then(function() {
return winIcecream();
}).then(function() {
console.log('finished');
})
Why the promises aren't chained like that the every .then word is after the previous promise? I mean, why for example .then is not immediately after removeGarbage() but it is after the cleanRoom().then(), how is it happening that the winIcecream() will run after resolving the removeGarbage promise?
Also do I need to type return declaring every promise like in the code above? If yes, why do I need to do so?
Your initial questions may be answered by rewriting things to variable assignments.
I'm using arrow function syntax to implicitly return the new expression here; if you're using regular functions, then yes, you do have to return the new chained promise if you want to run them in sequence.
const roomCleanedPromise = cleanRoom();
const roomCleanedAndGarbageTakenOutPromise = roomCleanedPromise.then(() => removeGarbage());
const roomCleanedAndGarbageTakenOutAndIcecreamWonPromise = roomCleanedAndGarbageTakenOutPromise.then(() => winIcecream());
const finishedPromise = roomCleanedAndGarbageTakenOutAndIcecreamWonPromise.then(() => console.log('finished'));
However things are easier written using the more modern async/await syntax - the YouTube tutorial you mention is a little outdated, perhaps.
async function cleanRoom() {
console.log('Cleaning room.');
// this could do other async things or just take a while
return {room: 'clean'}; // just to demonstrate a return value
}
async function removeGarbage() {
console.log('Removing garbage.');
// this could do other async things or just take a while
return {garbage: 'removed'};
}
// third function elided for brevity
async function doAllTheThings() {
const roomStatus = await cleanRoom();
const garbageStatus = await removeGarbage();
console.log('finished');
}
The purpose of using a fulfillment handler (the functions passed to then in your example) is to wait for the promise to be fulfilled and then, at that point, do something else.
The goal of that code (apparently) is to wait until the cleanRoom promise is fulfilled, then start the removeGarbage process, and then when that is fulfilled, start the winIcecream process. It's also worth noting that if cleanRoom's promise was rejected instead of being fulfilled, removeGarbage wouldn't happen at all, because it's in a fulfillment handler, not a rejection handler.
If you did this instead:
cleanRoom().then(function() { /*...*/ });
removeGarbage().then(function() { /*...*/ });
winIcecream().then(function() { /*...*/ });
...all three processes would be started immediately and run in parallel (to the extent whatever async process they're modelling can run in parallel with other JavaScript code). There'd be no coordination between them at all.
...how is it happening that the winIcecream() will run after resolving the removeGarbage promise...
then, catch, and finally create and return new promises. Those promises are fulfilled or rejected based on what happens to the promise they were called on and what happens in or is returned by their handler. So for example:
doThis()
.then(function() { return doThat(); })
.then(function() { console.log("done"); });
Let's call the promise from doThis() "Promise A". Calling then on it creates a new promise ("Promise B"), that will either be rejected (if Promise A is rejected) or will call its handler if Promise A is fulfilled. Promise B is resolved to whatever that handler returns. In the code above, suppose Promise A is fulfilled and doThat() returns a promise ("Promise C"). Now, Promise B is resolved to Promise C — whatever happens to Promise C is what will happen to Promise B. If Promise C is fulfilled, Promise B is fulfilled, and the second handler with the console.log is called.
The MDN article on using promises may be helpful.
Your suggestion / thinking I mean, why for example .then is not immediately after removeGarbage() but it is after the cleanRoom().then() is wrong.
Each promise ( .then ) is not execute immediately.
You can take a look at your example ( little edited by me )
const cleanRoom = () => new Promise((resolve, reject) => {
resolve();
});
const removeGarbage = () => new Promise((resolve, reject) => {
resolve();
});
const winIcecream = () => new Promise((resolve, reject) => {
resolve();
})
cleanRoom().then(function(){
console.log('second');
return removeGarbage();
}).then(function() {
return winIcecream();
}).then(function() {
console.log('finished');
})
console.log('first');
You should read more how the event loop works.
var p1 = new Promise(function(resolve, reject) {
setTimeout(() => resolve("first"), 5000);
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(() => resolve("second"), 2000);
});
var p3 = new Promise(function(resolve, reject) {
setTimeout(() => resolve("third"), 1000);
});
console.log("last to print");
p1.then(()=>p2).then(()=>p3).then(()=> console.log("last to be printed"))
As I was reading about promises, I know that I can print promises synchronous (in this case print: first, second, third, last to print) when I use async /await. Now I have also been reading that the same thing can be achieved using .then chaining and async/await is nothing 'special'. When I try to chain my promises, however, nothing happens except for the console.log of "last to be printed". Any insight would be great! Thanks!!
Edit to question:
var p1 = new Promise(function (resolve, reject) {
setTimeout(() => console.log("first"), 5000);
resolve("first resolved")
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(() => console.log("second"), 2000);
resolve("second resolved")
});
var p3 = new Promise(function (resolve, reject) {
setTimeout(() => console.log("third"), 0);
resolve("third resolved")
});
console.log("starting");
p1.then((val) => {
console.log("(1)", val)
return p2
}).then((val) => {
console.log("(2)", val)
return p3
}).then((val) => {
console.log("(3)", val)
})
Loggs:
starting
(1) first resolved
(2) second resolved
(3) third resolved
third
second
first
1: if executor function passed to new Promise is executed immediately, before the new promise is returned, then why are here promises resolved ()synchronously) first and after the setTimeouts (asynchronously) gets executed?
Return value vs. resolve promise:
var sync = function () {
return new Promise(function(resolve, reject){
setTimeout(()=> {
console.log("start")
resolve("hello") //--works
// return "hello" //--> doesnt do anything
}, 3000);
})
}
sync().then((val)=> console.log("val", val))
The executor function you pass to new Promise is executed immediately, before the new promise is returned. So when you do:
var p1 = new Promise(function(resolve, reject) {
setTimeout(() => resolve("first"), 5000);
});
...by the time the promise is assigned to p1, the setTimeout has already been called and scheduled the callback for five seconds later. That callback happens whether you listen for the resolution of the promise or not, and it happens whether you listen for resolution via the await keyword or the then method.
So your code starts three setTimeouts immediately, and then starts waiting for the first promise's resolution, and only then waiting for the second promise's resolution (it'll already be resolved, so that's almost immediate), and then waiting for the third (same again).
To have your code execute those setTimeout calls only sequentially when the previous timeout has completed, you have to not create the new promise until the previous promise resolves (using shorter timeouts to avoid lots of waiting):
console.log("starting");
new Promise(function(resolve, reject) {
setTimeout(() => resolve("first"), 1000);
})
.then(result => {
console.log("(1) got " + result);
return new Promise(function(resolve, reject) {
setTimeout(() => resolve("second"), 500);
});
})
.then(result => {
console.log("(2) got " + result);
return new Promise(function(resolve, reject) {
setTimeout(() => resolve("third"), 100);
});
})
.then(result => {
console.log("(3) got " + result);
console.log("last to print");
});
Remember that a promise doesn't do anything, and doesn't change the nature of the code in the promise executor. All a promise does is provide a means of observing the result of something (with really handy combinable semantics).
Let's factor out the common parts of those three promises into a function:
function delay(ms, ...args) {
return new Promise(resolve => {
setTimeout(resolve, ms, ...args);
});
}
Then the code becomes a bit clearer:
function delay(ms, ...args) {
return new Promise(resolve => {
setTimeout(resolve, ms, ...args);
});
}
console.log("starting");
delay(1000, "first")
.then(result => {
console.log("(1) got " + result);
return delay(500, "second");
})
.then(result => {
console.log("(2) got " + result);
return delay(100, "third");
})
.then(result => {
console.log("(3) got " + result);
console.log("last to print");
});
Now, let's put that in an async function and use await:
function delay(ms, ...args) {
return new Promise(resolve => {
setTimeout(resolve, ms, ...args);
});
}
(async() => {
console.log("starting");
console.log("(1) got " + await delay(1000, "first"));
console.log("(2) got " + await delay(500, "second"));
console.log("(3) got " + await delay(100, "third"));
console.log("last to print");
})();
Promises make that syntax possible, by standardizing how we observe asynchronous processes.
Re your edit:
1: if executor function passed to new Promise is executed immediately, before the new promise is returned, then why are here promises resolved ()synchronously) first and after the setTimeouts (asynchronously) gets executed?
There are two parts to that question:
A) "...why are here promises resolved ()synchronously) first..."
B) "...why are here promises resolved...after the setTimeouts (asynchronously) gets executed"
The answer to (A) is: Although you resolve them synchronously, then always calls its callback asynchronously. It's one of the guarantees promises provide. You're resolving p1 (in that edit) before the executor function returns. But the way you're observing the resolutions ensures that you observe the resolutions in order, because you don't start observing p2 until p1 has resolved, and then you don't start observing p3 until p2 is resolved.
The answer to (B) is: They don't, you're resolving them synchronously, and then observing those resolutions asynchronously, and since they're already resolved that happens very quickly; later, the timer callbacks run. Let's look at how you create p1 in that edit:
var p1 = new Promise(function (resolve, reject) {
setTimeout(() => console.log("first"), 5000);
resolve("first resolved")
});
What happens there is:
new Promise gets called
It calls the executor function
The executor function calls setTimeout to schedule a callback
You immediately resolve the promise with "first resolved"
new Promise returns and the resolved promise is assigned to p1
Later, the timeout occurs and you output "first" to the console
Then later you do:
p1.then((val) => {
console.log("(1)", val)
return p2
})
// ...
Since then always calls its callback asynchronously, that happens asynchronously — but very soon, because the promise is already resolved.
So when you run that code, you see all three promises resolve before the first setTimeout callback occurs — because the promises aren't waiting for the setTimeout callback to occur.
You may be wondering why you see your final then callback run before you see "third" in the console, since both the promise resolutions and the console.log("third") are happening asynchronously but very soon (since it's a setTimeout(..., 0) and the promises are all pre-resolved): The answer is that promise resolutions are microtasks and setTimeout calls are macrotasks (or just "tasks"). All of the microtasks a task schedules are run as soon as that task finishes (and any microtasks that they schedule are then executed as well), before the next task is taken from the task queue. So the task running your script does this:
Schedules a task for the setTimeout callback
Schedules a microtask to call p1's then callback
When the task ends, its microtasks are processed:
The first then handler is run, scheduling a microtask to run the second then handler
The second then handler runs and schedules a micro task to call the third then handler
Etc. until all the then handlers have run
The next task is picked up from the task queue. It's probably the setTimeout callback for p3, so it gets run and "third" appears in the console
Return value vs. resolve promise:
The part you've put in the question doesn't make sense to me, but your comment on this does:
I read that returning a value or resolving a promise is same...
What you've probably read is that returning a value from then or catch is the same as returning a resolved promise from then or catch. That's because then and catch create and return new promises when they're called, and if their callbacks return a simple (non-promise) value, they resolve the promise they create with that value; if the callback returns a promise, they resolve or reject the promise they created based on whether that promise resolves or rejects.
So for instance:
.then(() => {
return 42;
})
and
.then(() => {
return new Promise(resolve => resolve(42));
})
have the same end result (but the second one is less efficient).
Within a then or catch callback:
Returning a non-promise resolves the promise then/catch created with that value
Throwing an error (throw ...) rejects that promise with the value you throw
Returning a promise makes then/catch's promise resolve or reject based on the promise the callback returns
You cannot make asynchronous code execute synchronously.
Even async / await are just syntax that gives you a synchronous-style control flow inside a promise.
When I try to chain my promises, however, nothing happens except for the console.log of "last to be printed". Any insight would be great!
The other functions don't generate any output. That has nothing to do with them being in promises.
You start three timers (all at the same time), then log 'last to print', then chain some promises so that 'last to be printed' will print when all three promises resolve (5 seconds after you start them all going).
If you want the timers to run sequentially, then you have to initiate them only when the previous one has finished, and if you want to see what they resolve with then you have to write code that actually looks at that.
function p1() {
return new Promise(function(resolve, reject) {
setTimeout(() => resolve("first"), 5000);
});
}
function p2() {
return new Promise(function(resolve, reject) {
setTimeout(() => resolve("second"), 2000);
});
}
function p3() {
return new Promise(function(resolve, reject) {
setTimeout(() => resolve("third"), 1000);
});
}
function log(value) {
console.log("Previous promise resolved with " + value);
}
p1()
.then(log)
.then(p2)
.then(log)
.then(p3)
.then(log)
.then(() => console.log("last to be printed"));
Async/await is, arguably, neater:
function p1() {
return new Promise(function(resolve, reject) {
setTimeout(() => resolve("first"), 5000);
});
}
function p2() {
return new Promise(function(resolve, reject) {
setTimeout(() => resolve("second"), 2000);
});
}
function p3() {
return new Promise(function(resolve, reject) {
setTimeout(() => resolve("third"), 1000);
});
}
function log(value) {
console.log("Previous promise resolved with " + value);
}
(async function() {
log(await p1());
log(await p2());
log(await p3());
console.log("last to be printed");
}());
If you need to call an await but the function that contains that await doesn't have to be async, because you need, for example, a "number" and not a "Promise number ", you can do de next:
var ex: number = new Number(async resolve => {
var f = await funcionExample();
resolve(f);
}).valueOf();
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(...)
})
I'm trying to learn how to use promises, but am having trouble comprehending the chaining. I assume that with this code, both promises will run. Then when I call test.then() it should know that test has resolved and pass the resolve data to then().
Once that function finishes, it goes onto the next then(), repeating the same process with the test2 promise.
However, I can only get it to print out the first promise results, not the second. Any ideas what is missing here?
var test = new Promise(function(resolve, reject){
resolve('done1');
});
var test2 = new Promise(function(resolve, reject){
resolve('done2');
});
test
.then(function(data) {
console.log(data);
})
.then(test2)
.then(function(data) {
console.log(data);
});
Your first .then call is returning undefined, whereas any subsequent .then is expecting a returned promise. So you'd need to change your code to:
var test = new Promise(function(resolve, reject){
resolve('done1');
});
var test2 = new Promise(function(resolve, reject){
resolve('done2');
});
test
.then(function(data) {
console.log(data);
return test2;
})
.then(resultOfTest2 => doSomething)
.then(function(data) {
console.log(data);
});
You need to return next promise from the then callback:
test.then(function(data) {
console.log(data);
return test2;
}).then(function(data) {
console.log(data);
});
Summary:
The basic concept of promise chaining with promises is that every then / catch method on a fulfilled promise returns another promise. It works in the following manner:
When a promise is resolved the callback passed in the then method is called. The then method wraps the value which is returned in its callback in a resolved promise and returns this resolved promise.
When a promise is rejected the callback passed in the catch method is called. The catch method wraps the value which is returned in its callback in a rejected promise and returns this rejected promise.
Example:
Before fully understanding the concept of chaining multiple then methods it is important to know what exactly the return values of then and catch are. Take the following example:
let prom1 = new Promise((res, rej) => {
res('res');
});
const resolvedProm1 = prom1.then((val) => {return val});
// setTimeout needed for the promise to actually be resolved
setTimeout(() => console.log(resolvedProm1));
let prom2 = new Promise((res, rej) => {
rej('rej');
});
const resolvedProm2 = prom2.catch((err) => {throw err});
// setTimeout needed for the promise to actually be rejected
setTimeout(() => console.log(resolvedProm2));
We can observe the status of the promises in the chrome devtools:
What basically happens is that in a then or catch callback is the following:
Any value returned in a then or catch callback is wrapped in Promise.resolve() and a new resolved promise is returned.
Any error thrown in a then or catch callback is wrapped in Promise.reject() and a new rejected promise is returned.
Because we are getting returned a rejected or resolved promise object we can repeat the cycle and call the then or catch method on it again. For example:
const prom = new Promise((res, rej) => {
if (Math.random() > 0.5) {
res('success');
} else {
rej('error');
}
});
prom.then((val) => {
return val;
}).then((val) => {
return val
}).then((val) => {
console.log(val)
}).catch((err) => {
console.log('err');
})
This calling of then and catch methods which are executed in their respective order is called promise chaining. It is a very useful technique to make working with asynchronous code easier, especially if multiple asynchronous operations need to be performed which are dependend on each others data.
you need to return the other promise(test2) in the first promise (test1) to allow for chaining:
var test = new Promise(function(resolve, reject){
resolve('done1');
});
var test2 = new Promise(function(resolve, reject){
resolve('done2');
});
test
.then(function(data) {
console.log(data);
return test2;
});
You may also want to try -
let test = new Promise(function(resolve, reject){
resolve('done1');
});
let test2 = new Promise(function(resolve, reject){
resolve('done2');
});
try {
let logOne = test();
let logTwo = test2();
console.log(logOne);
console.log(logTwo);
} catch(error) {
console.error(error);
}
In this way, you can also properly handle any promise dependencies. For example if test one relied on test two's data your could -
try {
let logOne = test();
let logTwo = test2(logOne);
console.log(logOne);
console.log(logTwo);
} catch(error) {
console.error(error);
}