After watching the video, and reading in [Promise.then][1] i still don't understand how it works.
So i wrote an example, and printed to console but still with the output i can't understand how does it work.
I hope my question are clear enough, if not please tell me and i will try to elaborate them according to your responses.
This is my code:
const getCountryData = function (country) {
fetch(`https://restcountries.eu/rest/v2/name/${country}`)
.then(response => {
console.log('111111111111111');
if (!response.ok)
throw new Error(`Country not found (${response.status})`);
return response.json();
})
.then(data => {
console.log('22222222222');
const neighbour = data[0].borders[0];
if (!neighbour) return;
return fetch(`https://restcountries.eu/rest/v2/alpha/${neighbour}`);
})
.then(response => {
console.log('333333333')
if (!response.ok)
throw new Error(`Country not found (${response.status})`);
return response.json();
})
.then(data => console.log('44444444444'))
.catch(err => {
console.error(`${err} 💥💥💥`);
})
.finally(() => {
console.log('finalyyyy')
});
console.log("after all async calls");
};
I can't understand the following things:
If we look for example on the first then, it gets a callback as a parameter, the callback returns a Promise object. Which object calls the second then? The one that was returned from the first call to when or the one that have been returned from the callback?
I understand that the callback is "attached" to the fetch function. e.g when the fetch function will be finished, the callback will be executed. But i can't understand how and when the second, third and fourth then are called. The first callback contains also an async function ** response.json()**, so how and when exactly the second when will be started if he need the json.response from the first when?
If an exception occur at the first or second, how exactly the code knows to jump to catch function?
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
The then functions don't receive a callback, they receive the result of the previous async operation.
So the result of the fetch is passed to the first then, the result of response.json() is passed to the second then and so on...
In case of error the chain broke and it goes directly to the firs catch function.
Under the hood there are more, consider that the fetch and other operations in the then functions can be asynchronous, so what happens in the runtime is that the result of the fetch function is pushed in the event queue and when the event loop takes your next then function is extracted from the queue and passed to the function.
This article explain how it works internally: https://www.digitalocean.com/community/tutorials/understanding-the-event-loop-callbacks-promises-and-async-await-in-javascript
I will try to explain this in a different way, using async/await operators.
The equivalent of your getCountryData function is as follows
const getCountryData = async (country) => {
console.log("after all async calls"); // explanation for the this line below
try {
let response1 = await fetch(`https://restcountries.eu/rest/v2/name/${country}`);
// first then
console.log('111111111111111');
if (!response1.ok)
throw new Error(`Country not found (${response1.status})`);
let data1 = await response1.json();
// second then
console.log('22222222222');
const neighbour = data1[0].borders[0];
let response2;
if (neighbour)
response2 = await fetch(`https://restcountries.eu/rest/v2/alpha/${neighbour}`);
// third then
console.log('333333333');
if (!response2.ok)
throw new Error(`Country not found (${response2.status})`);
let data2 = await response2.json();
// fourth then
console.log('44444444444');
} catch (err) {
console.error(`${err} 💥💥💥`);
} finally {
console.log('finalyyyy');
}
}
First of all, you will see the console.log in your last line of function became the first line in my function. This is because the line is executed before the asynchronous function fetch completes.
As you can see, the last return variable from the fetch is available in the first then, and the last return variable from the then is available in the next then.
So for your first question, who calls the callback function in the next then? Obviously you can see from the async/await equivalent of your function, it is executed in sequence. The return result from the previous then will be the argument for the next then. Therefore in the second then, when you do return;, it will not stop executing the third then, but instead the third then will reveive undefined as the response argument.
As explained in answer to question 1, the then functions are executed in sequence automatically as long as there are no unhandled errors or rejected promise. However, for a second/third/forth then callback function to access the argument from the first then, it is not possible to access directly, unless you pass it along with every return from the subsequent then. This can be achieved easily by using then async/await operators instead of then.
How exactly does the code knows to jump to the catch function when an error is thrown? In an asynchronous function, throwing an error is almost equivalent to rejecting a promise. Think of it this way, every then callbacks are wrapped in a try/catch block that would call the function in the catch.
Edit: If you do fetch().then().then().catch(), all the errors or rejected promises will be handled in the catch function.
But if you do fetch().catch().then().then(), only the error or rejected promise from fetch will be handled in the catch function.
Related
I would like to run this code with babel:
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
inside an async function without await the first line. is this OK?
how else can I run something that I don't care?
Can I just fire the non-promisified function del('key',null) without a callback?
Yes, you can do that, and it will run the two asynchronous functions in parallel. You've just created a promise and thrown it away.
However, this means that when the promise is rejected you won't notice. You'll just get an unhandledRejection eventually which will crash your process if not handled.
Is this OK? How can I run something that I don't care?
Probably it's not OK. If you truly wouldn't care, you hadn't run it in the first place. So you should be clear and explicit what you care about (and what not):
do you want to wait? (for side effects)
do you need the result?
do you want to catch exceptions?
If you only want to wait and don't care for the result value, you can easily throw away the result:
void (await someAsyncFunction()); // or omit the void keyword,
// doesn't make a difference in an expression statement
If you don't care about exceptions, you can ignore them using
… someAsyncFunction().catch(function ignore() {}) …
You can throw that away, await it, do anything with it.
If you want the result, you have to await it. If you care about exceptions, but don't really want to wait, you may want to execute it in parallel with the following functions:
var [_, res] = await Promise.all([
someAsyncFunction(), // result is ignored, exceptions aren't
someOtherAsyncFunction()
]);
return res;
inside an async function without await the first line. is this OK?
Yes, there are cases where you'd want to do this which are perfectly reasonable. Especially where you don't care about the result - one example is an analytics tracking operation that should not interfere with business critical code.
how else can I run something that I don't care?
In many ways, however simply calling the promise function works. Your del without a callback would probably work in this case but some functions don't guard against not passing callbacks, so you can pass an empty function instead (.del('key', () => {})).
You do want to however make sure that you know about it failing, even if you don't want to disrupt the operation of code - so please consider adding a process.on("unhandledRejection', event handler to explicitly ignore these particular exceptions or suppress them via:
redisClient.delAsync('key').catch(()=>{});
Or preferably, something like:
redisClient.delAsync('key').catch(logErr);
From all the research I've made so far, I think it's fine to do it, as long as you guarantee that the function you are not awaiting for guarantees a way to handle its own errors in case that happens. For example, a try-catch wrapping the whole function body, like you see in the following snippet for the asyncFunction.
It doesn't matter if the function throws synchronously or asynchronously. It guarantees the your mainFunction will complete no matter what. That's the key point here.
If you don't guarantee that, you have to risks:
If it throws synchronously, your main function will not complete.
If it throws asynchronously, you'll get an unhandled excepction
// THIS IS SOME API CALL YOU DON'T WANT TO WAIT FOR
const mockAPI = () => {
console.log("From mockAPI");
return new Promise((resolve,reject) => {
setTimeout(() => reject("LATE THROW: API ERROR"), 500);
});
};
// THIS IS THE SOME ASYNC FUNCTION YOU CALL BUT NOT AWAIT FOR
const asyncFunction = async (syncThrow) => {
try {
console.log("Async function START");
if (syncThrow) throw new Error("EARLY THROW");
await mockAPI();
console.log("Async function DONE");
}
catch(err) {
console.log("From async function catch");
console.log(err.message || err);
return;
}
};
// THIS IS YOUR MAIN FUNCTION
const mainFunction = async (syncThrow) => {
try {
console.clear();
console.log("Main function START");
asyncFunction(syncThrow);
console.log("Main function DONE <<< THAT'S THE IMPORTANT PART");
}
catch(err) {
console.log("THIS WILL NEVER HAPPEN");
console.log(err);
}
};
<div>
<button onClick="mainFunction(true)">Sync throw</button>
<button onClick="mainFunction(false)">Async throw</button>
</div>
Not in Node.js.
Node does not wait for ever-pending Promises. If other tasks are already completed and there is nothing left in the event loop, the Node process will be terminated even though there exists pending promise.
For the following script, if someOtherAsyncFunction() get resolved in 5 seconds, but redisClientAsync.delAsync('key') takes 10 seconds to execute, the Node process will be terminated after 5 seconds in theory, before the first line is resolved.
async function doSomething() {
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
}
await doSomething();
I would like to run this code with babel:
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
inside an async function without await the first line. is this OK?
how else can I run something that I don't care?
Can I just fire the non-promisified function del('key',null) without a callback?
Yes, you can do that, and it will run the two asynchronous functions in parallel. You've just created a promise and thrown it away.
However, this means that when the promise is rejected you won't notice. You'll just get an unhandledRejection eventually which will crash your process if not handled.
Is this OK? How can I run something that I don't care?
Probably it's not OK. If you truly wouldn't care, you hadn't run it in the first place. So you should be clear and explicit what you care about (and what not):
do you want to wait? (for side effects)
do you need the result?
do you want to catch exceptions?
If you only want to wait and don't care for the result value, you can easily throw away the result:
void (await someAsyncFunction()); // or omit the void keyword,
// doesn't make a difference in an expression statement
If you don't care about exceptions, you can ignore them using
… someAsyncFunction().catch(function ignore() {}) …
You can throw that away, await it, do anything with it.
If you want the result, you have to await it. If you care about exceptions, but don't really want to wait, you may want to execute it in parallel with the following functions:
var [_, res] = await Promise.all([
someAsyncFunction(), // result is ignored, exceptions aren't
someOtherAsyncFunction()
]);
return res;
inside an async function without await the first line. is this OK?
Yes, there are cases where you'd want to do this which are perfectly reasonable. Especially where you don't care about the result - one example is an analytics tracking operation that should not interfere with business critical code.
how else can I run something that I don't care?
In many ways, however simply calling the promise function works. Your del without a callback would probably work in this case but some functions don't guard against not passing callbacks, so you can pass an empty function instead (.del('key', () => {})).
You do want to however make sure that you know about it failing, even if you don't want to disrupt the operation of code - so please consider adding a process.on("unhandledRejection', event handler to explicitly ignore these particular exceptions or suppress them via:
redisClient.delAsync('key').catch(()=>{});
Or preferably, something like:
redisClient.delAsync('key').catch(logErr);
From all the research I've made so far, I think it's fine to do it, as long as you guarantee that the function you are not awaiting for guarantees a way to handle its own errors in case that happens. For example, a try-catch wrapping the whole function body, like you see in the following snippet for the asyncFunction.
It doesn't matter if the function throws synchronously or asynchronously. It guarantees the your mainFunction will complete no matter what. That's the key point here.
If you don't guarantee that, you have to risks:
If it throws synchronously, your main function will not complete.
If it throws asynchronously, you'll get an unhandled excepction
// THIS IS SOME API CALL YOU DON'T WANT TO WAIT FOR
const mockAPI = () => {
console.log("From mockAPI");
return new Promise((resolve,reject) => {
setTimeout(() => reject("LATE THROW: API ERROR"), 500);
});
};
// THIS IS THE SOME ASYNC FUNCTION YOU CALL BUT NOT AWAIT FOR
const asyncFunction = async (syncThrow) => {
try {
console.log("Async function START");
if (syncThrow) throw new Error("EARLY THROW");
await mockAPI();
console.log("Async function DONE");
}
catch(err) {
console.log("From async function catch");
console.log(err.message || err);
return;
}
};
// THIS IS YOUR MAIN FUNCTION
const mainFunction = async (syncThrow) => {
try {
console.clear();
console.log("Main function START");
asyncFunction(syncThrow);
console.log("Main function DONE <<< THAT'S THE IMPORTANT PART");
}
catch(err) {
console.log("THIS WILL NEVER HAPPEN");
console.log(err);
}
};
<div>
<button onClick="mainFunction(true)">Sync throw</button>
<button onClick="mainFunction(false)">Async throw</button>
</div>
Not in Node.js.
Node does not wait for ever-pending Promises. If other tasks are already completed and there is nothing left in the event loop, the Node process will be terminated even though there exists pending promise.
For the following script, if someOtherAsyncFunction() get resolved in 5 seconds, but redisClientAsync.delAsync('key') takes 10 seconds to execute, the Node process will be terminated after 5 seconds in theory, before the first line is resolved.
async function doSomething() {
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
}
await doSomething();
I have a simple code in JavaScript that execute a request in an API and return the response, simple. But in this case I will have thousands of requests. So, which one of the code options will perform better, and why. Also which one is recommended as good pratices these days?
First options is using the .then to resolve the promises and the seccond one is using async / await.
In my tests the two options had very similar results without significant differences, but I'm not sure in scale.
// Using then
doSomething(payload) {
const url = 'https://link-here/consultas';
return this.axios.get(url, {
params: {
token: payload.token,
chave: payload.chave,
},
}).then(resp => resp.data);
}
// Using Async / await
async doSomething(payload) {
const url = 'https://link-here/consultas';
const resp = await this.axios.get(url, {
params: {
token: payload.token,
chave: payload.chave,
},
});
return resp.data;
}
Any explanation will be of great value.
From a performance point of view, await is just an internal version of .then() (doing basically the same thing). The reason to choose one over the other doesn't really have to do with performance, but has to do with desired coding style or coding convenience. Certainly, the interpreter has a few more opportunities to optimize things internally with await, but its unlikely that should be how you decide which to use. If all else was equal, I would choose await for the reason cited above. But, I'd first choose which made the code simpler to write and understand and maintain and test.
Used properly, await can often save you a bunch of lines of code making your code simpler to read, test and maintain. That's why it was invented.
There's no meaningful difference between the two versions of your code. Both achieve the same result when the axios call is successful or has an error.
Where await could make more of a convenience difference is if you had multiple successive asynchronous calls that needed to be serialized. Then, rather than bracketing them each inside a .then() handler to chain them properly, you could just use await and have simpler looking code.
A common mistake with both await and .then() is to forget proper error handling. If your error handling desire in this function is to just return the rejected promise, then both of your versions do that identically. But, if you have multiple async calls in a row and you want to do anything more complex than just returning the first rejection, then the error handling techniques for await and .then()/.catch() are quite different and which seems simpler will depend upon the situation.
There should be some corrections in this thread. await and .then are going to give very different results, and should be used for different reasons.
await will WAIT for something, and then continue to the next line. It's also the simpler of the two because it behaves mechanically more like synchronous behavior. You do step #1, wait, and then continue.
console.log("Runs first.");
await SomeFunction();
console.log("Runs last.");
.then splits off from the original call and starts operating within its own scope, and will update at a time the original scope cannot predict. If we can put semantics aside for a moment, it's "more asynchronous," because it leaves the old scope and branches off into a new one.
console.log("Runs first.");
SomeFunction().then((value) => {console.log("Runs last (probably). Didn't use await on SomeFunction().")})
console.log("Runs second (probably).");
As more explanation to #user280209 answer let's consider the following function which returns promise and compare its execution with .then() and async await.
function call(timeout) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`This call took ${timeout} seconds`);
resolve(true);
}, timeout * 1000);
});
}
With .then()
(async () => {
call(5).then((r) => {
console.log(r);
});
await call(2); //This will print result first
await call(1);
})();
When running the above call the logs will be
This call took 2 seconds
This call took 1 seconds
This call took 5 seconds
true
As we can see .then() didn't pause the execution of its below line until it completes.
With async/wait
(async () => {
await call(5); //This will print result first
await call(2);
await call(1);
})();
When run the above function logs will be
This call took 5 seconds
This call took 2 seconds
This call took 1 seconds
So I think if your promise's result won't be used in the following lines, .then() may be better.
For those saying await blocks the code until the async call returns you are missing the point. "await" is syntactic sugar for a promise.then(). It is effectively wrapping the rest of your function in the then block of a promise it is creating for you. There is no real "blocking" or "waiting".
run();
async function run() {
console.log('running');
makePromises();
console.log('exiting right away!');
}
async function makePromises() {
console.log('make promise 1');
const myPromise = promiseMe(1)
.then(msg => {
console.log(`What i want to run after the promise is resolved ${msg}`)
})
console.log('make promise 2')
const msg = await promiseMe(2);
console.log(`What i want to run after the promise is resolved via await ${msg}`)
}
function promiseMe(num: number): Promise<string> {
return new Promise((resolve, reject) => {
console.log(`promise`)
resolve(`hello promise ${num}`);
})
}
The await line in makePromises does not block anything and the output is:
running
make promise 1
promise
make promise 2
promise
exiting right away!
What i want to run after the promise is resolved hello promise 1
What i want to run after the promise is resolved via await hello promise 2
Actually.
Await/Async can perform more efficiently as Promise.then() loses the scope in which it was called after execution, you are attaching a callback to the callback stack.
What it causes is: The system now has to store a reference to where the .then() was called. In case of error it has to correctly point to where the error happens, otherwise, without the scope (as the system resumed execution after called the Promise, waiting to comeback to the .then() later) it isn't able to point to where the error happened.
Async/Await you suspend the exection of the method where it is being called thus preserving reference.
If we just consider performance(time taken) then it actually depends on whether your operations are serial or parallel. If your tasks are serial then there will be no difference between await and .then. But if your tasks are parallel then .then will take less time. Consider the following example
let time = Date.now();
// first task takes 1.5 secs
async function firstTask () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
},1500)
})
}
// second task takes 2 secs
async function secondTask () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
},2000)
})
}
// using await
async function call(){
const d1 = await firstTask();
const d2 = await secondTask();
console.log(Date.now()-time, d1+d2)
}
call()
// using .then
async function call2(){
let d1=null,d2=null;
firstTask().then(data => {
d1=data;
if(d2){
console.log(Date.now()-time, d1+d2);
}
})
secondTask().then(data => {
d2=data;
if(d1){
console.log(Date.now()-time, d1+d2);
}
})
}
call2()
Here are the two tasks, first takes 1.5 secs and second takes 2 secs. Call function uses await where as call2 function uses .then . The output is as follows
From call2 2012 3
From call 3506 3
I hope it helps.
As far as I understand .then() and await are not the same thing. An async function won't proceed with the next command until the promise is resolved/rejected since it's basically an implementation of generators. On the contrast, in the case of .then(), the execution of the function will proceed with the next command and the resolve/reject callback will be executed "when there's time" aka when the current event loop (not entirely sure about that part) will be completed.
tldr; on a single promise await and .then() behave similarly but when one promise needs another one to be resolved first then the two of them behave entirely different
Many answer have been provided to this question already. However, to point out key information in the answers above and from my understanding, note below point:
only use await when not handling error return
if no crucial need for error handling use await instead
use .then .catch if returned error message or data is crucial for debugging / or proper error handling instead of try catch for await
Choose any prefer method from code sample below
const getData = (params = {name: 'john', email: 'ex#gmail.com'}) => {
return axios.post(url, params);
}
// anywhere you want to get the return data
// using await
const setData = async () => {
const data = await getData();
}
// to handle error with await
const setData = async () => {
try {
const data = await getData();
}
catch(err) {
console.log(err.message);
}
}
// using .then .catch
const setData = () => {
var data;
getData().then((res) => {
data = res.data; console.log(data)
}).catch((err) => {
console.log(err.message);
});
}
I would like to compose two functions which return Promises into a function which returns a Promise.
I really want to do this for asynchronous system calls, but to simplify things, for now I'm playing with Promises wrapped around setTimeout. Most of the system calls can be sent out and returned in any order, but in a few instances, data from one system call is needed to create a second system call.
In the enclosed example, I want to send out alerts with an interval of 5 seconds, then 3 seconds, then 10 seconds. When I run the code, things run in the wrong order.
I commented out code where I was able to run the two calls chained with ".then". The commented out code works as desired. But I haven't yet figured out how to put that into a function which returns a promise, and still have it work.
I'm interested in doing this using plain JavaScript only. After I understand how to do that, I'll look at answers which do the same using a library.
jsfiddle
// Returns a Promise.
// I purposely chose not to use the callback function inside setTimeout in order to demonstrate
// how to combine functions which return Promises, and normal functions which don't.
function sleep(delay, callbackFunction) {
return new Promise(function(resolve) {
setTimeout(resolve.bind(null, callbackFunction), delay)
});
}
// Normal function, does not return a promise.
const myPrint = (message, value) => {
alert(message + " " + value);
};
// A function combining a promise and then a normal function.
// Returns a Promise.
const sleepPrint = (delay) => {
return sleep(delay)
.then(() => {
myPrint("sleepPrint()", delay);
});
};
// A function combining two functions which return promises.
// Returns a Promise.
const sleepPrintSleepPrint = (delay1, delay2) => {
return sleepPrint(delay1)
.then(sleepPrint(delay2));
};
alert("Begin");
/*
// Run a Pomise function, then a normal function.
const delay = 4000;
sleep(delay)
.then(() => {
myPrint("init", delay);
});
*/
const firstDelay = 5000;
const secondDelay = 3000;
/*
// Call sleepPrint() which runs a Promise function then a normal function.
// After all that finishes, call another Promise function, than another normal function.
// This code works as desired, but the code at the bottom doesn't!
sleepPrint(firstDelay)
.then(() => {
return sleep(secondDelay)
})
.then(() => {
myPrint("After sleepPrint() and sleep()", secondDelay);
});
*/
// Comine two calls to sleepPrint() into a new functiion which returns a Promise, sleepPrintsleepPrint().
// After all that finishes, call another Promise function, than another normal function.
// I want this to have 5 second delay and then a 3 second delay, but they are not running in that order.
const thirdDelay = 10000;
sleepPrintSleepPrint(firstDelay, secondDelay)
.then(() => {
return sleep(thirdDelay)
})
.then(() => {
myPrint("After sleepPrintSleepPrint()", thirdDelay);
});
Remember how functions work (in any programming language). This:
const sleepPrintSleepPrint = (delay1, delay2) => {
return sleepPrint(delay1)
.then(sleepPrint(delay2));
};
Is the same as this:
const sleepPrintSleepPrint = (delay1, delay2) => {
var x = sleepPrint(delay2);
return sleepPrint(delay1)
.then(x);
};
What you are doing is calling both functions simultaneously. What I think you intend is to call the second function once then resolves. So you need to do this:
const sleepPrintSleepPrint = (delay1, delay2) => {
return sleepPrint(delay1)
.then(function(){return sleepPrint(delay2)});
};
Or if you prefer arrow function syntax:
const sleepPrintSleepPrint = (delay1, delay2) => {
return sleepPrint(delay1)
.then(() => sleepPrint(delay2));
};
#slebetman supplied the correct code, and he explained the issue and the fix correctly at a high level. This is my understanding of his answer in more detail. This should be a comment to his answer, but it's too big.
The JavaScript interpreter makes two passes through code which contains chained ".then"s.
The first pass happens right away without any waiting. The interpreter looks at the argument(s) to .then, and does something different if it sees a function or an expression. Functions are placed onto the call stack, and associated with a promise. Expressions are evaluated, and if the completion value is a function, the function is placed onto the call stack, and associated with a promise.
In the second pass, after a promise has been resolved, the associated function from the call stack is evaluated.
In production code, chained .then's should always follow a specific pattern. The pattern is that the argument to ".then" is one function which returns a second callback function. In the first pass, nothing very interesting happens, the first function is executed, and the return value is the second callback function. The second callback function is placed onto the call stack and associated with a promise. In the second pass, after the associated promise has been resolved, the second callback function is executed.
But in this example, I am breaking the pattern in order to learn and demonstrate how chained .then's work.
Below is an example which does not use the pattern, and exposes the two pass behavior.
This is talking about the two arguments to .then, the success callback function, and the failure callback function.
"If one or both arguments are omitted or are provided non-functions, then then will be missing the handler(s), but will not generate any errors."
If you give a function to ".then", then in the first pass, the function is placed on the call stack, but nothing else happens, and in the second pass, the function is executed.
But if you give an expression to ".then", then in the first pass, the expression is evaluated. If the completion value is a function, it is placed onto the call stack, and executed in the second pass. If the completion value is not a function, in the second pass, no further action is taken, and no error is generated.
First Pass (The JavaScript interpreter runs these things immediateley.)
Kick off sleepPrint(firstDelay). Let it run in the background.
Immediately go to the next ".then".
Print the first message. Put nothing on the call stack.
Immediately go to the next ".then".
Put the function () => sleepPrint(secondDelay) onto the call stack, and associate it with the preceding promise.
Immediately go to the next ".then".
Print the second message. Put nothing on the call stack.
Second Pass (The JavaScript interpreter runs each of these steps from the call stack much later, after the corresponding promise has been resolved.)
Wait until sleepPrint(firstDelay) prints the third message, and is resolved, then go to the next item on the call stack.
There's nothing on the call stack for this slot. There's no promise to resolve, so go to the next item on the call stack. No error is generated.
Execute function () => sleepPrint(secondDelay) which prints the fourth message. Wait for the promise to be resolved, then go to the next item on the call stack.
There's nothing on the call stack for this slot. There's no promise to resolve. No error is generated.
jsFiddle
// Returns a Promise.
// I purposely chose not to use the callback function inside setTimeout in order to demonstrate
// how to combine functions which return Promises, and normal functions which don't.
function sleep(delay, callbackFunction) {
return new Promise(function(resolve) {
setTimeout(resolve.bind(null, callbackFunction), delay)
});
}
// Normal function, does not return a promise.
const myPrint = (message, value) => {
alert(message + " " + value);
};
// A function combining a promise and then a normal function.
// Returns a Promise.
const sleepPrint = (delay) => {
return sleep(delay)
.then(() => {
myPrint("sleepPrint()", delay);
});
};
alert("Begin");
const firstDelay = 5000;
const secondDelay = 3000;
// This message is printed third.
sleepPrint(firstDelay)
.then(
myPrint("This message is printed first", 0)
)
.then(
// This message is printed fourth.
() => {sleepPrint(secondDelay)}
)
.then(
myPrint("This message is printed second", 0)
);
I would like to run this code with babel:
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
inside an async function without await the first line. is this OK?
how else can I run something that I don't care?
Can I just fire the non-promisified function del('key',null) without a callback?
Yes, you can do that, and it will run the two asynchronous functions in parallel. You've just created a promise and thrown it away.
However, this means that when the promise is rejected you won't notice. You'll just get an unhandledRejection eventually which will crash your process if not handled.
Is this OK? How can I run something that I don't care?
Probably it's not OK. If you truly wouldn't care, you hadn't run it in the first place. So you should be clear and explicit what you care about (and what not):
do you want to wait? (for side effects)
do you need the result?
do you want to catch exceptions?
If you only want to wait and don't care for the result value, you can easily throw away the result:
void (await someAsyncFunction()); // or omit the void keyword,
// doesn't make a difference in an expression statement
If you don't care about exceptions, you can ignore them using
… someAsyncFunction().catch(function ignore() {}) …
You can throw that away, await it, do anything with it.
If you want the result, you have to await it. If you care about exceptions, but don't really want to wait, you may want to execute it in parallel with the following functions:
var [_, res] = await Promise.all([
someAsyncFunction(), // result is ignored, exceptions aren't
someOtherAsyncFunction()
]);
return res;
inside an async function without await the first line. is this OK?
Yes, there are cases where you'd want to do this which are perfectly reasonable. Especially where you don't care about the result - one example is an analytics tracking operation that should not interfere with business critical code.
how else can I run something that I don't care?
In many ways, however simply calling the promise function works. Your del without a callback would probably work in this case but some functions don't guard against not passing callbacks, so you can pass an empty function instead (.del('key', () => {})).
You do want to however make sure that you know about it failing, even if you don't want to disrupt the operation of code - so please consider adding a process.on("unhandledRejection', event handler to explicitly ignore these particular exceptions or suppress them via:
redisClient.delAsync('key').catch(()=>{});
Or preferably, something like:
redisClient.delAsync('key').catch(logErr);
From all the research I've made so far, I think it's fine to do it, as long as you guarantee that the function you are not awaiting for guarantees a way to handle its own errors in case that happens. For example, a try-catch wrapping the whole function body, like you see in the following snippet for the asyncFunction.
It doesn't matter if the function throws synchronously or asynchronously. It guarantees the your mainFunction will complete no matter what. That's the key point here.
If you don't guarantee that, you have to risks:
If it throws synchronously, your main function will not complete.
If it throws asynchronously, you'll get an unhandled excepction
// THIS IS SOME API CALL YOU DON'T WANT TO WAIT FOR
const mockAPI = () => {
console.log("From mockAPI");
return new Promise((resolve,reject) => {
setTimeout(() => reject("LATE THROW: API ERROR"), 500);
});
};
// THIS IS THE SOME ASYNC FUNCTION YOU CALL BUT NOT AWAIT FOR
const asyncFunction = async (syncThrow) => {
try {
console.log("Async function START");
if (syncThrow) throw new Error("EARLY THROW");
await mockAPI();
console.log("Async function DONE");
}
catch(err) {
console.log("From async function catch");
console.log(err.message || err);
return;
}
};
// THIS IS YOUR MAIN FUNCTION
const mainFunction = async (syncThrow) => {
try {
console.clear();
console.log("Main function START");
asyncFunction(syncThrow);
console.log("Main function DONE <<< THAT'S THE IMPORTANT PART");
}
catch(err) {
console.log("THIS WILL NEVER HAPPEN");
console.log(err);
}
};
<div>
<button onClick="mainFunction(true)">Sync throw</button>
<button onClick="mainFunction(false)">Async throw</button>
</div>
Not in Node.js.
Node does not wait for ever-pending Promises. If other tasks are already completed and there is nothing left in the event loop, the Node process will be terminated even though there exists pending promise.
For the following script, if someOtherAsyncFunction() get resolved in 5 seconds, but redisClientAsync.delAsync('key') takes 10 seconds to execute, the Node process will be terminated after 5 seconds in theory, before the first line is resolved.
async function doSomething() {
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
}
await doSomething();