What is the difference between:
async function subfunc(){
await something()
}
async function func(){
subfunc() // subfunc is not awaited but something inside it is
}
func()
and
async function subfunc(){
await something()
}
async function func(){
await subfunc() // subfunc is awaited and something inside it is also
}
func()
It sounds like it produces the same result, where am I wrong?
It sounds like it produces the same result, where am I wrong?
The results are very different.
Without await, the promise returned by subfunc is not awaited, so func settles its promise without waiting for the promise from subfunc to settle. Several ramifications of that:
subfunc's work won't be done yet when func's promise is settled.
func can't use the fulfillment value of subfunc's promise (though your code using await doesn't use it either, so that may not matter to you).
if subfunc's promise is rejected, it doesn't cause rejection of func's promise; func's promise will already be fulfilled. In fact, nothing will handle the rejection of subfunc's promise, which will at a minimum trigger a console warning about an unhandled rejction or at a maxiumum could terminate the process the code is running in (Node.js does this by default, for instance).
(Re that third one: It happens that in the example you've given, nothing is handling rejection of func's promise either, but at least code calling func has the opportunity to handle rejection. Without the await of subfunc's promise, code calling func can't avoid a possible unhandled rejection.)
You can see some of that in these two examples (be sure to look int he real browser console):
Without await:
function something(flag) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (flag) {
console.log("`subfunc` promise fulfilled");
resolve();
} else {
console.log("`subfunc` promise rejected");
reject(new Error("Some error here"));
}
}, 100);
});
}
async function subfunc(flag) {
await something(flag);
}
async function func(flag) {
subfunc(flag); // No `await`
}
async function test(flag) {
console.log("-");
console.log(
`Calling \`func\` to test ${flag ? "fulfillment" : "rejection"}:`
);
try {
await func(flag);
console.log("`func` promise fulfilled");
} catch (error) {
console.log("`func` promise rejected: ", error.message);
}
}
console.log("WITHOUT `await`:");
test(true).then(() => {
setTimeout(() => {
test(false);
}, 500);
});
.as-console-wrapper {
max-height: 100% !important;
}
Notice the
Uncaught (in promise) Error: Some error here
or similar in the real browser console. That's the unhandled rejection.
With await:
function something(flag) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (flag) {
console.log("`subfunc` promise fulfilled");
resolve();
} else {
console.log("`subfunc` promise rejected");
reject(new Error("Some error here"));
}
}, 100);
});
}
async function subfunc(flag) {
await something(flag);
}
async function func(flag) {
await subfunc(flag);
}
async function test(flag) {
console.log("-");
console.log(
`Calling \`func\` to test ${flag ? "fulfillment" : "rejection"}:`
);
try {
await func(flag);
console.log("`func` promise fulfilled");
} catch (error) {
console.log("`func` promise rejected: ", error.message);
}
}
console.log("WITH `await`:");
test(true).then(() => {
setTimeout(() => {
test(false);
}, 500);
});
.as-console-wrapper {
max-height: 100% !important;
}
In a comment you asked:
this is what I don't get, we you execute subfunc without await, something() is awaited anyway no?
That's a very good observation. Yes, something() is awaited in subfunc (so subfunc doesn't settle its promise until the promise it gets from something() settles), but that doesn't make func wait for subfunc to settle its promise.
It may help to walk through the execution of your first code block (the one without await in func), starting where the main script calls func. (Please note that I've omitted some details for clarity.)
func calls subfunc:
subfunc calls something and gets back a value, let's call it pSomething. (Let's assume the normal case: pSomething is a promise, and it's still pending.¹)
subfunc uses await on pSomething:
subfunc creates a promise, let's call it pSubfunc.
await attaches fulfilment and rejection handlers to pSomething. (More on what these handlers do later.)
subfunc returns pSubfunc.
Now that subfunc has returned, func returns, since it's not doing anything with subfunc's promise:
func creates a promise (let's call it pFunc).
func fulfills pFunc with undefined.
func returns pFunc.
It's possible nothing further happens because pSomething is never settled, but let's assume at some point it gets fulfilled:
pSomething's settlement calls the fulfilment handler attached in subfunc in Step 1.2.2. That fulfilment handler contains the (implicit) code following the await in subfunc, which is return;. That code fulfills pSubfunc with undefined.
So as you can see, you're right that pSomething is awaited by subfunc, and so pSubfunc doesn't settle until pSomething settles. But func doesn't await pSubfunc, so func fulfills pFunc as soon as subfunc returns its still-pending promise.
And that's the crucial difference in the code: func doesn't wait for subfunc to finish its work, even though subfunc waits for something to finish its work.
¹ If pSomething weren't a promise, await would create and fulfill a new promise using the non-promise value it received as the fulfilment value and use that instead, which would change a couple of minor details in the explanation above, but not in ways that your code could easily observe.
Related
I have a very simple code snippet like this
async function neverResolve() {
return new Promise(() => {
console.log("This promise will never resolve");
});
}
(async () => {
try {
console.log("START");
// neverResolve().then().catch(); // uncommenting this line works as expected
await neverResolve();
await new Promise((resolve) => setTimeout(() => resolve(), 5000));
console.log("END");
} catch (error) {
console.log("ERR: ", error);
}
})();
Why the above function doesn't wait for 5 second and print's the END.
It automatically terminates after printing
START
This promise will never resolve
But if we execute the same function but with a .then() construct, I get the expected result.
async function neverResolve() {
return new Promise(() => {
console.log("This promise will never resolve");
});
}
(async () => {
try {
console.log("START");
neverResolve().then().catch();
await new Promise((resolve) => setTimeout(() => resolve(), 5000));
console.log("END");
} catch (error) {
console.log("ERR: ", error);
}
})();
This is what's happening in your case:
Your program starts it's execution from an anonymous IIFE async function, as this is an async function, it immediately returns a Promise to a global scope.So the execution of anonymous IIFE is deferred .
You can easily validate this by adding a console.log("OK"); at the end of your IIFE invocation, which is printed to the console
Node keeps a reference count of things like timers and network requests. When you make a network, or other async request, set a timer, etc. Node adds on to this ref count. When the times/request resolve Node subtracts from the count. Ref. video link
So what happens inside your IIFE is:
console.log("START"); <--- gets printed to console
await neverResolve(); Here things get's interesting, this await call will defer the execution and blocks until the callback are executed, either resolve or reject.
But in this case there are no callbacks registered and nodejs will think that it finished processing all the request and will terminate the process.
neverResolve neither resolves or rejects, so the program hangs indefinitely at the await. Consider abstracting the timeout functionality in its own generic function, timeout -
const sleep = ms =>
new Promise(r => setTimeout(r, ms));
const timeout = (p, ms) =>
Promise.race([
p,
sleep(ms).then(() => { throw Error("timeout") })
]);
const neverResolve = () => new Promise(() => {});
(async function() {
try {
console.log("connecting...");
await timeout(neverResolve(), 2000);
}
catch (err) {
console.error(err);
}
})();
In the first example await neverResolve(); waits forever and never resolves as stated. Javascript can go off and do other things in other tasks while waiting for this.
In the second example by adding .then() you've told javascript to continue processing code below. Typically, all the code below would either be inside the callback for then() or catch() which would then create the same pause you're seeing.
There are very deliberate reasons for these nuances that allow you to send a fetch request, do other work and then come back to see if the fetch is done later. See my comments in this marked up and slightly modified example.
async function slowFetch() {
return new Promise((resolve) => {
// a fetch statement that takes forever
fetch().then(data => resolve(data));
});
}
(async () => {
try {
console.log("START");
// start the slow fetch right away
const dataPromise = slowFetch();
// do some other tasks that don't take as long
await new Promise((resolve) => setTimeout(() => resolve(), 5000));
// wait for the data to arrive
const data = await dataPromise;
// do something with the data like fill in the page.
console.log("END");
} catch (error) {
console.log("ERR: ", error);
}
})();
By doing neverResolve().then().catch(); what you actually did is;
async function neverResolve() {
return new Promise(() => {
console.log("This promise will never resolve");
});
}
(async () => {
try {
console.log("START");
(async function(){
try {
await neverResolve()
}
catch{
}
})();
await new Promise((resolve) => setTimeout(() => resolve(), 5000));
console.log("END");
} catch (error) {
console.log("ERR: ", error);
}
})();
The inner async IIFE runs just like neverResolve().then().catch(); does. You should not mix promises with async await abstraction.
Why the above function doesn't wait for 5 second and print's the END. It automatically terminates after printing
Because your code never gets past this:
await neverResolve();
Since neverResolve() returns a promise that never resolves or rejects, this function is forever suspended at that line and the lines of code after this statement in the function never execute.
await means that the function execution should be suspended indefinitely until the promise you are awaiting either resolves or rejects.
But if we execute the same function but with a .then() construct, I get the expected result.
When you change the await to this:
neverResolve().then().catch();
The function execute is NOT suspended at all. It executes neverResolve(). That returns a promise which it then calls .then() on. That call to .then() just registers a callback with the promise (to be called later when the promise resolves). That returns another promise which it then calls .catch() on which just registers a callback with the promise (to be called later if/when the promise rejects).
Now, you aren't even passing a callback in either case, so those .then() and .catch() have nothing to actually do, but even if you did pass a callback to each of them, then they would just register that callback and immediately return. .then() and .catch() are not blocking. They just register a callback and immediately return. So, after they return, then next lines of code in the function will execute and you will get the output you were expecting.
Summary
await suspends execution of the function until the promise you are awaiting resolves or rejects.
.then() and .catch() just register callbacks for some future promise state change. They do not block. They do not suspend execution of the function. They register a callback and immediately return.
I am new to typescript / javascript, so I don't know much about promises. Here is my use-case: I am creating three different promises inside my cloud-function and then returning it with Promise.all([promise1, promise2, promise3]). Each of these promises are created inside a function with "return Promise...".
My question is, when I add ".catch" inside these functions, will Promise.all still work?. Does it make any difference returning someServiceThatCreatesPromise() with and without .catch()?
Code
export async function myCloudFirestoreFunction() {
try {
const myFirstPromise = createFirstPromise()
const mySecondPromise = createSecondPromise()
const thirdPromise = createThirdPromise()
return Promise.all([
myFirstPromise,
mySecondPromise,
myThirdPromise
]);
} catch (err) {
functions.logger.err(`Something bad happened, See: ${(err as Error).message}`
}
}
// Difference with and without `.catch`?
function createFirstPromise() {
return someServiceThatCreatesPromise().catch((err) => { // LOGGING });
}
// Difference with and without `.catch`?
function createSecondPromise() {
return someServiceThatCreatesPromise().catch((err) => { // LOGGING });
}
// Difference with and without `.catch`?
function createThirdPromise() {
return someServiceThatCreatesPromise().catch((err) => { // LOGGING });
}
Adding .catch inside createNPromise won't affect anything assuming all your Promises resolve and do not reject.
However, if one of the Promises rejects and you catch it within the .catch method, then it won't work as you're expecting unless you re-throw that error and catch it again inside the try/catch in your myCloudFirestoreFunction function.
async function myCloudFirestoreFunction() {
try {
const result = await Promise.all([
createFirstPromise(),
createSecondPromise()
]);
} catch (error) {
console.log({ error });
}
}
function createFirstPromise() {
return Promise.reject("Oof").catch((e) => {
// do work, e.g. log, then
// pass the error forward so that it can be caught
// inside the caller
throw e;
});
}
function createSecondPromise() {
return Promise.resolve("value");
}
myCloudFirestoreFunction();
Alternatively, you just catch errors inside the caller (myCloudFirestoreFunction) instead of catching them separately.
async function myCloudFirestoreFunction() {
const result = await Promise.all([
createFirstPromise(),
createSecondPromise()
]).catch((err) => console.log({ err }));
}
function createFirstPromise() {
return Promise.reject("Oof");
}
function createSecondPromise() {
return Promise.resolve("value");
}
myCloudFirestoreFunction();
when I add ".catch" inside these functions, will Promise.all still work?
Calling catch() on a promise does not in any way change the way the original promise works. It is just attaching a callback that gets invoked when the first promise becomes rejected, and also returning another promise that resolves after the original promise is fulfilled or rejected.
Does it make any difference returning someServiceThatCreatesPromise() with and without .catch()?
It would not make any difference to the code that depends on the returned promise. Both the original promise and the one returned by catch() will tell downstream code when the original work is done by becoming fulfilled.
I suggest reading comprehensive documentation on promises, for example:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
There is a diagram in there that you can follow to understand what happens at each turn. Also in that document, you will read:
Processing continues to the next link of the chain even when a .then() lacks a callback function that returns a Promise object. Therefore, a chain can safely omit every rejection callback function until the final .catch().
Given the code samples below, is there any difference in behavior, and, if so, what are those differences?
return await promise
async function delay1Second() {
return (await delay(1000));
}
return promise
async function delay1Second() {
return delay(1000);
}
As I understand it, the first would have error-handling within the async function, and errors would bubble out of the async function's Promise. However, the second would require one less tick. Is this correct?
This snippet is just a common function to return a Promise for reference.
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
Most of the time, there is no observable difference between return and return await. Both versions of delay1Second have the exact same observable behavior (but depending on the implementation, the return await version might use slightly more memory because an intermediate Promise object might be created).
However, as #PitaJ pointed out, there is one case where there is a difference: if the return or return await is nested in a try-catch block. Consider this example
async function rejectionWithReturnAwait () {
try {
return await Promise.reject(new Error())
} catch (e) {
return 'Saved!'
}
}
async function rejectionWithReturn () {
try {
return Promise.reject(new Error())
} catch (e) {
return 'Saved!'
}
}
In the first version, the async function awaits the rejected promise before returning its result, which causes the rejection to be turned into an exception and the catch clause to be reached; the function will thus return a promise resolving to the string "Saved!".
The second version of the function, however, does return the rejected promise directly without awaiting it within the async function, which means that the catch case is not called and the caller gets the rejection instead.
As other answers mentioned, there is likely a slight performance benefit when letting the promise bubble up by returning it directly — simply because you don’t have to await the result first and then wrap it with another promise again. However, no one has talked about tail call optimization yet.
Tail call optimization, or “proper tail calls”, is a technique that the interpreter uses to optimize the call stack. Currently, not many runtimes support it yet — even though it’s technically part of the ES6 Standard — but it’s possible support might be added in the future, so you can prepare for that by writing good code in the present.
In a nutshell, TCO (or PTC) optimizes the call stack by not opening a new frame for a function that is directly returned by another function. Instead, it reuses the same frame.
async function delay1Second() {
return delay(1000);
}
Since delay() is directly returned by delay1Second(), runtimes supporting PTC will first open a frame for delay1Second() (the outer function), but then instead of opening another frame for delay() (the inner function), it will just reuse the same frame that was opened for the outer function. This optimizes the stack because it can prevent a stack overflow (hehe) with very large recursive functions, e.g., fibonacci(5e+25). Essentially it becomes a loop, which is much faster.
PTC is only enabled when the inner function is directly returned. It’s not used when the result of the function is altered before it is returned, for example, if you had return (delay(1000) || null), or return await delay(1000).
But like I said, most runtimes and browsers don’t support PTC yet, so it probably doesn’t make a huge difference now, but it couldn’t hurt to future-proof your code.
Read more in this question: Node.js: Are there optimizations for tail calls in async functions?
Noticeable difference: Promise rejection gets handled at different places
return somePromise will pass somePromise to the call site, and await somePromise to settle at call site (if there is any). Therefore, if somePromise is rejected, it will not be handled by the local catch block, but the call site's catch block.
async function foo () {
try {
return Promise.reject();
} catch (e) {
console.log('IN');
}
}
(async function main () {
try {
let a = await foo();
} catch (e) {
console.log('OUT');
}
})();
// 'OUT'
return await somePromise will first await somePromise to settle locally. Therefore, the value or Exception will first be handled locally. => Local catch block will be executed if somePromise is rejected.
async function foo () {
try {
return await Promise.reject();
} catch (e) {
console.log('IN');
}
}
(async function main () {
try {
let a = await foo();
} catch (e) {
console.log('OUT');
}
})();
// 'IN'
Reason: return await Promise awaits both locally and outside, return Promise awaits only outside
Detailed Steps:
return Promise
async function delay1Second() {
return delay(1000);
}
call delay1Second();
const result = await delay1Second();
Inside delay1Second(), function delay(1000) returns a promise immediately with [[PromiseStatus]]: 'pending. Let's call it delayPromise.
async function delay1Second() {
return delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
Async functions will wrap their return value inside Promise.resolve()(Source). Because delay1Second is an async function, we have:
const result = await Promise.resolve(delayPromise);
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
Promise.resolve(delayPromise) returns delayPromise without doing anything because the input is already a promise (see MDN Promise.resolve):
const result = await delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
await waits until the delayPromise is settled.
IF delayPromise is fulfilled with PromiseValue=1:
const result = 1;
ELSE is delayPromise is rejected:
// jump to catch block if there is any
return await Promise
async function delay1Second() {
return await delay(1000);
}
call delay1Second();
const result = await delay1Second();
Inside delay1Second(), function delay(1000) returns a promise immediately with [[PromiseStatus]]: 'pending. Let's call it delayPromise.
async function delay1Second() {
return await delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
Local await will wait until delayPromise gets settled.
Case 1: delayPromise is fulfilled with PromiseValue=1:
async function delay1Second() {
return 1;
}
const result = await Promise.resolve(1); // let's call it "newPromise"
const result = await newPromise;
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: 1
const result = 1;
Case 2: delayPromise is rejected:
// jump to catch block inside `delay1Second` if there is any
// let's say a value -1 is returned in the end
const result = await Promise.resolve(-1); // call it newPromise
const result = await newPromise;
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: -1
const result = -1;
Glossary:
Settle: Promise.[[PromiseStatus]] changes from pending to resolved or rejected
This is a hard question to answer, because it depends in practice on how your transpiler (probably babel) actually renders async/await. The things that are clear regardless:
Both implementations should behave the same, though the first implementation may have one less Promise in the chain.
Especially if you drop the unnecessary await, the second version would not require any extra code from the transpiler, while the first one does.
So from a code performance and debugging perspective, the second version is preferable, though only very slightly so, while the first version has a slight legibility benefit, in that it clearly indicates that it returns a promise.
In our project, we decided to always use 'return await'.
The argument is that "the risk of forgetting to add the 'await' when later on a try-catch block is put around the return expression justifies having the redundant 'await' now."
Here is a typescript example that you can run and convince yourself that you need that "return await"
async function test() {
try {
return await throwErr(); // this is correct
// return throwErr(); // this will prevent inner catch to ever to be reached
}
catch (err) {
console.log("inner catch is reached")
return
}
}
const throwErr = async () => {
throw("Fake error")
}
void test().then(() => {
console.log("done")
}).catch(e => {
console.log("outer catch is reached")
});
here i leave some code practical for you can undertand it the diferrence
let x = async function () {
return new Promise((res, rej) => {
setTimeout(async function () {
console.log("finished 1");
return await new Promise((resolve, reject) => { // delete the return and you will see the difference
setTimeout(function () {
resolve("woo2");
console.log("finished 2");
}, 5000);
});
res("woo1");
}, 3000);
});
};
(async function () {
var counter = 0;
const a = setInterval(function () { // counter for every second, this is just to see the precision and understand the code
if (counter == 7) {
clearInterval(a);
}
console.log(counter);
counter = counter + 1;
}, 1000);
console.time("time1");
console.log("hello i starting first of all");
await x();
console.log("more code...");
console.timeEnd("time1");
})();
the function "x" just is a function async than it have other fucn
if will delete the return it print "more code..."
the variable x is just an asynchronous function that in turn has another asynchronous function, in the main of the code we invoke a wait to call the function of the variable x, when it completes it follows the sequence of the code, that would be normal for "async / await ", but inside the x function there is another asynchronous function, and this returns a promise or returns a" promise "it will stay inside the x function, forgetting the main code, that is, it will not print the" console.log ("more code .. "), on the other hand if we put" await "it will wait for every function that completes and finally follows the normal sequence of the main code.
below the "console.log (" finished 1 "delete the" return ", you will see the behavior.
Given the code samples below, is there any difference in behavior, and, if so, what are those differences?
return await promise
async function delay1Second() {
return (await delay(1000));
}
return promise
async function delay1Second() {
return delay(1000);
}
As I understand it, the first would have error-handling within the async function, and errors would bubble out of the async function's Promise. However, the second would require one less tick. Is this correct?
This snippet is just a common function to return a Promise for reference.
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
Most of the time, there is no observable difference between return and return await. Both versions of delay1Second have the exact same observable behavior (but depending on the implementation, the return await version might use slightly more memory because an intermediate Promise object might be created).
However, as #PitaJ pointed out, there is one case where there is a difference: if the return or return await is nested in a try-catch block. Consider this example
async function rejectionWithReturnAwait () {
try {
return await Promise.reject(new Error())
} catch (e) {
return 'Saved!'
}
}
async function rejectionWithReturn () {
try {
return Promise.reject(new Error())
} catch (e) {
return 'Saved!'
}
}
In the first version, the async function awaits the rejected promise before returning its result, which causes the rejection to be turned into an exception and the catch clause to be reached; the function will thus return a promise resolving to the string "Saved!".
The second version of the function, however, does return the rejected promise directly without awaiting it within the async function, which means that the catch case is not called and the caller gets the rejection instead.
As other answers mentioned, there is likely a slight performance benefit when letting the promise bubble up by returning it directly — simply because you don’t have to await the result first and then wrap it with another promise again. However, no one has talked about tail call optimization yet.
Tail call optimization, or “proper tail calls”, is a technique that the interpreter uses to optimize the call stack. Currently, not many runtimes support it yet — even though it’s technically part of the ES6 Standard — but it’s possible support might be added in the future, so you can prepare for that by writing good code in the present.
In a nutshell, TCO (or PTC) optimizes the call stack by not opening a new frame for a function that is directly returned by another function. Instead, it reuses the same frame.
async function delay1Second() {
return delay(1000);
}
Since delay() is directly returned by delay1Second(), runtimes supporting PTC will first open a frame for delay1Second() (the outer function), but then instead of opening another frame for delay() (the inner function), it will just reuse the same frame that was opened for the outer function. This optimizes the stack because it can prevent a stack overflow (hehe) with very large recursive functions, e.g., fibonacci(5e+25). Essentially it becomes a loop, which is much faster.
PTC is only enabled when the inner function is directly returned. It’s not used when the result of the function is altered before it is returned, for example, if you had return (delay(1000) || null), or return await delay(1000).
But like I said, most runtimes and browsers don’t support PTC yet, so it probably doesn’t make a huge difference now, but it couldn’t hurt to future-proof your code.
Read more in this question: Node.js: Are there optimizations for tail calls in async functions?
Noticeable difference: Promise rejection gets handled at different places
return somePromise will pass somePromise to the call site, and await somePromise to settle at call site (if there is any). Therefore, if somePromise is rejected, it will not be handled by the local catch block, but the call site's catch block.
async function foo () {
try {
return Promise.reject();
} catch (e) {
console.log('IN');
}
}
(async function main () {
try {
let a = await foo();
} catch (e) {
console.log('OUT');
}
})();
// 'OUT'
return await somePromise will first await somePromise to settle locally. Therefore, the value or Exception will first be handled locally. => Local catch block will be executed if somePromise is rejected.
async function foo () {
try {
return await Promise.reject();
} catch (e) {
console.log('IN');
}
}
(async function main () {
try {
let a = await foo();
} catch (e) {
console.log('OUT');
}
})();
// 'IN'
Reason: return await Promise awaits both locally and outside, return Promise awaits only outside
Detailed Steps:
return Promise
async function delay1Second() {
return delay(1000);
}
call delay1Second();
const result = await delay1Second();
Inside delay1Second(), function delay(1000) returns a promise immediately with [[PromiseStatus]]: 'pending. Let's call it delayPromise.
async function delay1Second() {
return delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
Async functions will wrap their return value inside Promise.resolve()(Source). Because delay1Second is an async function, we have:
const result = await Promise.resolve(delayPromise);
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
Promise.resolve(delayPromise) returns delayPromise without doing anything because the input is already a promise (see MDN Promise.resolve):
const result = await delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
await waits until the delayPromise is settled.
IF delayPromise is fulfilled with PromiseValue=1:
const result = 1;
ELSE is delayPromise is rejected:
// jump to catch block if there is any
return await Promise
async function delay1Second() {
return await delay(1000);
}
call delay1Second();
const result = await delay1Second();
Inside delay1Second(), function delay(1000) returns a promise immediately with [[PromiseStatus]]: 'pending. Let's call it delayPromise.
async function delay1Second() {
return await delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
Local await will wait until delayPromise gets settled.
Case 1: delayPromise is fulfilled with PromiseValue=1:
async function delay1Second() {
return 1;
}
const result = await Promise.resolve(1); // let's call it "newPromise"
const result = await newPromise;
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: 1
const result = 1;
Case 2: delayPromise is rejected:
// jump to catch block inside `delay1Second` if there is any
// let's say a value -1 is returned in the end
const result = await Promise.resolve(-1); // call it newPromise
const result = await newPromise;
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: -1
const result = -1;
Glossary:
Settle: Promise.[[PromiseStatus]] changes from pending to resolved or rejected
This is a hard question to answer, because it depends in practice on how your transpiler (probably babel) actually renders async/await. The things that are clear regardless:
Both implementations should behave the same, though the first implementation may have one less Promise in the chain.
Especially if you drop the unnecessary await, the second version would not require any extra code from the transpiler, while the first one does.
So from a code performance and debugging perspective, the second version is preferable, though only very slightly so, while the first version has a slight legibility benefit, in that it clearly indicates that it returns a promise.
In our project, we decided to always use 'return await'.
The argument is that "the risk of forgetting to add the 'await' when later on a try-catch block is put around the return expression justifies having the redundant 'await' now."
Here is a typescript example that you can run and convince yourself that you need that "return await"
async function test() {
try {
return await throwErr(); // this is correct
// return throwErr(); // this will prevent inner catch to ever to be reached
}
catch (err) {
console.log("inner catch is reached")
return
}
}
const throwErr = async () => {
throw("Fake error")
}
void test().then(() => {
console.log("done")
}).catch(e => {
console.log("outer catch is reached")
});
here i leave some code practical for you can undertand it the diferrence
let x = async function () {
return new Promise((res, rej) => {
setTimeout(async function () {
console.log("finished 1");
return await new Promise((resolve, reject) => { // delete the return and you will see the difference
setTimeout(function () {
resolve("woo2");
console.log("finished 2");
}, 5000);
});
res("woo1");
}, 3000);
});
};
(async function () {
var counter = 0;
const a = setInterval(function () { // counter for every second, this is just to see the precision and understand the code
if (counter == 7) {
clearInterval(a);
}
console.log(counter);
counter = counter + 1;
}, 1000);
console.time("time1");
console.log("hello i starting first of all");
await x();
console.log("more code...");
console.timeEnd("time1");
})();
the function "x" just is a function async than it have other fucn
if will delete the return it print "more code..."
the variable x is just an asynchronous function that in turn has another asynchronous function, in the main of the code we invoke a wait to call the function of the variable x, when it completes it follows the sequence of the code, that would be normal for "async / await ", but inside the x function there is another asynchronous function, and this returns a promise or returns a" promise "it will stay inside the x function, forgetting the main code, that is, it will not print the" console.log ("more code .. "), on the other hand if we put" await "it will wait for every function that completes and finally follows the normal sequence of the main code.
below the "console.log (" finished 1 "delete the" return ", you will see the behavior.
I am not able to catch the Exception/Error that is happening inside of the Resolve promise.
Can someone explain if there is a way to catch this error:
createAsync = function() {
var resolve, reject,
promise = new Promise(function(res, rej) {
resolve = res;
reject = rej;
});
promise.resolve = resolve;
promise.reject = reject;
return promise;
};
promise = createAsync()
promise.then(function myresolver(){
throw Error('wow')
})
// Catching the error here
try {
promise.resolve()
} catch(err) {
console.log( 'GOTCHA!!', err );
}
EDIT:
Let me explain me better, I'm trying to create an API and the user only have access to the promise resolve/reject part:
// This is my API user does not have access to this part
promise = createAsync()
promise
.then(function(fun) {
if (typeof fun != 'function')
throw Error('You must resolve a function as argument')
}).catch(function(err){
// Some internal api tasks...
return Promise.reject(err)
})
Now the solution I would like to give him, but does not work:
// Here is where the user can resolve the promise.
// But must be able to catch the error
promise.catch(function(err){
console.log("GOTCHA", err)
})
promise.resolve('This must be a function but is a string');
Any ideas?
More info:
Yes is an antipattern for the common usage. This is a remote procedure call API, so the user must be able to reject or resolve. And the calls are async so the best approach is to use promises. I wouldn't say is an antipattern for this case. Actually is the only pattern. (I can't use Async/Await)
Let me explain me better, I'm trying to create an API and the user only have access to the promise resolve/reject part.
Now the solution I would like to give him does not work.
Any ideas?
Don't put everything onto that initial promise object. It sounds what you really need to expose to your user is a) a way to resolve (which includes rejection) and b) a way to get the result (promise) back. So all you need to do is give him a function:
function createAsync() {
var resolve;
var promise = new Promise(function(r) { resolve = r; })
.then(function(fun) {
if (typeof fun != 'function')
throw Error('You must resolve a function as argument')
}).catch(function(err){
// Some internal api tasks...
throw err;
});
return function(value) {
resolve(value);
return promise;
};
}
and he'd use it like
var start = createAsync();
// possibly later
start('This must be a function but is a string').catch(function(err){
console.log("GOTCHA", err)
});
// or
start(Promise.reject(new Error('doomed to fail'))).catch(…);
but actually this pattern is still convoluted and much too complicated. Simply give your createAsync function a parameter (that might receive a promise if the user needs to delay passing the fun) and be done with it:
function createAsync(valu) {
return Promise.resolve(val).then(function(fun) {
if (typeof fun != 'function')
throw Error('You must resolve a function as argument')
}).catch(function(err){
// Some internal api tasks...
throw err;
});
}
createAsync('This must be a function but is a string').catch(function(err) {
console.log("GOTCHA", err)
});
// or
createAsync(new Promise(function(resolve) {
// later
resolve('This must be a function but is a string');
})).catch(function(err) {
console.log("GOTCHA", err)
});
Warning: as pointed out by #torazaburo "Exporting" the resolve and reject functions provided to the executor (the function passed to the promise constructor), in your case by polluting the promise object with additional properties, is an anti-pattern. The entire point of the design of the promise constructor is that resolving and rejecting is isolated within the executor. Your approach allows any random person who happens to acquire one of your promise objects to resolve or reject it, which is almost certainly not good program design.
With that warning in mind, your call to promise.resolve() doesn't reject the promise ... your promise.then ... throw will "reject" THAT promise (the one returned by .then) not the one in promise
rewrite your code as follows:
promise = createAsync()
promise // the call to promise.resolve will resolve THIS promise, so .then will execute
.then(function myresolver(){
throw Error('wow')
}) // .then returns a NEW promise, in this case rejected with Error('wow')
.catch(function(e) {
console.log("GOTCHA", e)
});
promise.resolve()
and you'll see the expected GOTCHA in the console
Jaromanda's answer already explained that you must use a rejection handler or catch to handle asynchronous errors using promises.
Also make sure to read torazaburo's comment explaining why your approach is an anti-pattern.
If you really must use this approach and also require try/catch for some reason, ES7 gives you a way via async functions
(async function f() {
const createAsync = function() {
var resolve, reject,
promise = new Promise(function(res, rej) {
resolve = res;
reject = rej;
});
promise.resolve = resolve;
promise.reject = reject;
return promise;
};
const promise = createAsync();
promise.resolve();
// Catching the error here
try {
await promise.then(function() {
throw Error('wow')
});
} catch (err) {
console.log('GOTCHA!!', err);
}
})()