When we have an async function that returns a variable, and then .then() is run...
What does .then() accept as parameters?
Is it the variable that async returns?
For example, consider this node.js example:
const js_package = require('random_js_package');
var random_variable;
let js_function = async () => {
random_variable = js_package();
return random_variable;
}
js_function().then((value) => {
for (var i=0; i<value.length; i++){
console.log(value[i]);
}
});
In this case, is the variable called value inside .then() what the async function returns?
In other words, is value the same as random_variable?
The then function accepts two arguments:
a function to run if the promise is resolved
a function to run if the promise is rejected
The first function you pass to it needs to accept one argument:
The resolved value of a promise
Meanwhile, async functions returns:
A Promise which will be resolved with the value returned by the async function, or rejected with an uncaught exception thrown from within the async function.
So, aside from you confusing the then function with the function you pass to it: Yes.
When you declare a function with async the function will always return a promise. So if you don't explicitly return a promise from the async function then javascript will wrap the value you return in a promise. .then() always takes a function which has a parameter which is the resolved value of a promise. So your code is correct the way it is written. In your code value will equal random_variable. Behind the scenes javascript is returning a promise from js_function which is why using .then((value) => works.
Related
I test the fetch API with a callback, but my function returns "Promise State: Pending", and I don't understand why :
async function getJson(url, callback) {
await fetch(url)
.then(async function(response) {
return await response.json()
})
.then(function(data) {
console.log(data)
callback(data)
})
}
let getData = {
getAll: async function() {
await getJson('js/getJson/data.json', function(data) {
console.log(data.photographers) //OK
let test = data.photographers
return test
})
}
}
console.log(getData.getAll()); //return promise pending
Thanks
General advice for asynchronous and promise-based programming listed below...
Here's what getJson() should look like:
function getJson(url, callback) {
return fetch(url).then(function(response) {
return response.json();
});
}
Just return the promise that fetch() already returned - don't try to convert to a callback. Converting a promise-based interface to a callback interface is going backwards in terms of programming usability. Promises are much better to program with than plain callbacks for asynchronous programming.
Here's how getAll() can then use it:
const getData = {
getAll: async function() {
const data = await getJson('js/getJson/data.json');
return data.photographers;
}
}
Or, equally good (and equivalent to the above example) would be this:
const getData = {
getAll: function() {
return getJson('js/getJson/data.json').then(data => {
return data.photographers;
});
}
}
await has significant advantages when you are sequencing more than one asynchronous operation, but usually doesn't really help much when there's just one asynchronous operation. It's not wrong to use it then, it just doesn't really offer much help.
And, here's how one would call getAll():
getData.getAll().then(photographers => {
console.log(photographers);
}).catch(err => {
console.log(err);
});
General Advice and Explanation:
1. Read and study how async and await work with promises. Only use it once you understand that and then you will only be using await when you are awaiting a promise. await does nothing useful unless you are awaiting a promise so if you're awaiting a function call, then that function must be returning a promise and that promise must be connected to when the asynchronous operations in that function are completed.
2. Don't mix plain callbacks and promises. If you are programming with a promise interface, use that promise - never convert it to a plain callback. Return a promise from your function and let the caller use that. Among the many, many reasons that promises were invented is that control flow in non-simple asynchronous operations is massively simpler with promises (particularly asynchronous error handling and error propagation to higher levels).
3. Convert plain callbacks to promises. If you encounter an asynchronous operation that you want to use in a world where there are other promise-based asynchronous operations (such as fetch()), then wrap the plain callback into a promise interface so you are only mixing promise-based calls with other promise-based calls. Much, much simpler to code reliably.
4. async functions ALWAYS return a promise. That's how they are built internal to Javascript. So, a caller of an async function always gets back a promise as the return value. That promise will initially be in the pending state. It will be resolved sometime in the future and the eventual resolved value of the promise will be whatever value is returned from the outer scope of the async function. If there's no return statement in the outer scope of the async function, then the resolved value will be undefined as it is with both your `async functions.
5. A caller gets a resolved value from a promise with .then() or await. Those are the only two ways to get a resolved value out of a promise. So, any caller of an async function that wants some value back from it needs to use .then() or await to get that value.
6. If you have a promise-based operation inside a function and you wish to return it's resolved value from your function, then just return that promise from the function. That will allow the caller to use that promise to get the value. See my getJson() implementation above for how simple that can be.
7. Avoid return await fn() and use return fn() instead. If you're using return await fn(), then you're already in an async function and thus the function is already returning a promise. So, avoid the extra await as it doesn't do anything useful and just use return fn(). If fn() returns a value that value will become the resolved value of the promise that your async function returned. If fn() returns a promise, then the resolved value of that promise will become the resolved value of the promise that your async function returned.
8. Returning a value from a .then() handler becomes the resolved value of the parent promise. In the second getData() example above that uses .then() internally, the return data.photographers; statement sets the resolved value of the parent promise to data.photographers. So, any caller of getData() will find that data.photographers becomes the resolved value of the promise that getData() returns.
9. Returning a promise from a .then() handler chains the promises and the resolved value of the promise you return becomes the resolved value of the parent promise. Essentially, returning a promise from a .then() causes the parent promise to wait for the newly returned promise to resolve and it then gets its resolved value from that newly returned promise. You can see this in play in the getJson() function where response.json() returns a new promise that resolves to the json-parsed body of the http request. That resolved value will become the resolved value of the promise that the function returned.
10. Don't pass a callback when expecting a promise back. If you're passing a callback to some asynchronous function, then most of the time that function will not be returning a promise because many asynchronous APIs these days accept either a callback or return a promise, but don't do both at the same time. So, when looking to use await, make absolutely sure the function you're awaiting is returning a promise. When in doubt, look at the doc. If the doc is unclear look at the code for the function itself or run an experiment to see what the return value actually is. As an example, most of the mongodb asynchronous APIs will return a promise if you do NOT pass a callback to them, but will not return a promise if you do pass the callback. Use one or the other, not both.
It works, although it's not really what I wanted. Because I thought I could store my result outside of functions in a variable. But it seems that this is not possible in fact.
All this and due to the fact that I have to give an evaluation for my training. Fetch is not mandatory.
In the first version of my code, I didn't use it. I just did classes and function synchronously by loading my JSON into a variable and adding it to a script tag.
However, I wanted to do some tests because then I have to use the design pattern factory method. And I just tested and it works with your code. Thank you for taking the time to respond to me at such a late hour.
//we instantiate the factory
let factory = new identityFactory
getData.getAll().then(photographers => {
let identity = photographers
console.log(identity);
//we pass the identity in the factory
let newIdentity = factory.createIdentity(identity,'all')
console.log(newIdentity);
showIdentity(newIdentity)
}).catch(err => {
console.log(err);
});
$ cat x.js
async function print() {
console.log("abc");
}
print();
$ nodejs x.js
abc
How can it be?! print() returns a Promise object that isn't awaited, is it? If it is not awaited, then why is the console.log executed?
yeah, like choz said ,async functions returns a promise, even if you haven't defined a promise in your code.
i think this returned promise turns fulfilled after all promises in await statement get resolved.
i test it in the following code, it also returns promise, which only turns fulfilled after all promises go resolved(after 3000ms in this case):
async function print2() {
await console.log("abc")
await new Promise((res, rej) => {
setTimeout(() => {res(33)},3000)
})
await new Promise((res, rej) => {
setTimeout(() => {res(33)},50)
})
}
You could say that an empty function itself, returns undefined (Which actually doesn't return anything). Take a look at the sample below;
function print() {}
var returnVal = print(); // undefined
You'll notice that returnVal is undefined.
Now, if we have something in the body of test() there, but you still don't pass any return value, then returnVal will still be undefined.
function print() {
console.log('abc');
}
var returnVal = print(); // undefined
So, in order for a function to have a return value, one simply just needs to return it.
function print() {
console.log('abc');
return 1;
}
var returnVal = print(); // 1
When you conver it to async function. Quoting from MDN
The body of an async function can be thought of as being split by zero
or more await expressions. Top-level code, up to and including the
first await expression (if there is one), is run synchronously. In
this way, an async function without an await expression will run
synchronously. If there is an await expression inside the function
body, however, the async function will always complete asynchronously.
Code after each await expression can be thought of as existing in a
.then callback. In this way a promise chain is progressively
constructed with each reentrant step through the function. The return
value forms the final link in the chain.
Now, based on the information above, here's what we know that refer to your question;
Your print() does not return anything, which should be undefined
The async function will complete asynchronously, meaning it will always return a Promise. Either pending, fulfilled or rejected
So, to say it in your question, here's what your function actually does;
async function print() {
console.log("abc"); // Prints 'abc'
}
// Function above is equivalent to;
function printEquivalentInNonAsync() {
console.log("abc"); // Prints 'abc'
}
var returnVal = print(); // `returnVal` is `undefined`
And, to answer your question
If it is not awaited, then why is the console.log executed?
Regardless the async function is awaited, it still executes! - Awaits just to ensure the line execution is halted until the Asynchronous function (Promise) has reached fulfilled or rejected. Note that, the first state of a Promise is pending.
This question already has answers here:
Async function returning promise, instead of value
(3 answers)
Closed 2 years ago.
I have two time taking functions -> getExcelFileStream and loadWorkbook
I want the function structure to return a response(pass/failure) only after both these subfunctions have been executed.
But it always returns a promise pending clause.
async function parseToJson(data) {
const excelFileStream = await getExcelFileStream(data.url);
const dataRows = await loadWorkbook(excelFileStream, data.orgId);
return dataRows;
}
exports.uploadExcel = function uploadExcel(data) {
return parseToJson(data);
};
According to the doc (Syntax > Return value)
A Promise which will be resolved with the value returned by the async function, or rejected with an exception thrown from, or uncaught within, the async function.
So it should be
exports.uploadExcel = async function uploadExcel(data) {
return await parseToJson(data)
}
It is not possible to make an asynchronous code synchronous in javascript, and with javascript being single threaded, you can't write code to wait for the promise without blokcing the execution of the code handling the promise itself.
I am guessing when you're calling your function uploadExcel, you don't have any await or a .then() so you get a promise instead of the value of the resolved promise.
Unfortunately, You only have two options:
1- Let async/await propagates all the way up in your code
2- Make all your code synchronous, and that would be a very bad idea.
So I ran into this a few times now and I've always just ignored it.
In TypeScript when I code:
async function someAsyncFunc(): void {
const prom = await somePromise();
}
It complains to me that async function should always have the return type of: Promise<T>. So what it wants from me is:
async function someAsyncFunc(): Promise<void>
I know that, but this method does not return anything. Up to this point, I've just always given in, and used the type Promise<void> but this will lead to bugs as TypeScript now thinks this function returns a promise, won't it?
Had an idea whilst writing this question and tested it.. turned out to be true. Check out my own answer in case your wondering :)
Error message so it will hopefully be index by google:
The return type of an async function or method must be the global Promise<T> type.
An async function is consider an extension of the Promise paradigm.
For javascript/typescript know that the return of a specific function is asynchronous is because it return a Promise. This means that the return type of an ansynchronous function is always Promise. Then, you can wrap a value into that promise which can be void, number, string, another promise, etc.
From MDN:
An asynchronous function is a function which operates asynchronously
via the event loop, using an implicit Promise to return its result.
More over:
An async function can contain an await expression that pauses the
execution of the async function and waits for the passed Promise's
resolution, and then resumes the async function's execution and
returns the resolved value. Remember, the await keyword is only valid
inside async functions.
Resuming: The aysnc/await in functions are syntax sugar to facilitate the code readability.
Whist writing this question the solution came to my mind. So now I'm answering this myself in case anyone else is wondering.
When you create an async function JavaScript implicitly returns a Promise (which then will be void). Much like if you're writing a normal void function you don't have to put return; at the end, it just does return.
async function test() {};
// Will return a resolved void promise
// the code snipped result only shows "{}" check it out in
// the browser console and you'll see
console.log(test());
Async functions will always return a promise. The result of your answer to your question should print Promise{<state>: "<finished>"} instead of void. You need to await the async function to see the void response
async function test() {};
console.log(test());
becomes
async function test() {};
console.log(await test());
I'm a bit confused about the MDN documentation of async await. These docs use the .then() syntax to respond when an async function has resolved:
mdn example
async function add() {
const b = await resolveAfter2Seconds(30); // timer
return b;
}
add().then(v => {
console.log(v);
});
But in my own code, I don't use .then() and it still works asynchronously. The flow is not blocked. So why would you use .then() ?
async code without .then
function start(){
console.log("starting!")
let d = loadData()
console.log("this message logs before loadData returns!")
console.log(d) // this shows: Promise - Pending because d has not yet returned
}
async function loadData() {
const response = await fetch("https://swapi.co/api/films/");
const json = await response.json();
console.log("data loaded!")
return json;
}
First of all, all async functions return a Promise, so if you want to get the returned value from that async operation, you'll either need to use then or await inside an async function.
MDN uses .then because the add async function is being called outside an async function scope, so it can't use await globally to catch the data from the promise.
In your example, you get the same, an instance of Promise as the return of your loadData async function, if you define the start function also as async, you can use let d = await loadData(), if it's not async, you can use .then (which is the Promise API).
Async function declaration async function loadData() returns AsyncFunction which is executed asynchronously and always returns a Promise.
Basically all the code you put inside the async function someFunctionName will be executed inside that Promise and when you return some value inside that function – it will resolve that promise.
So that then() call is to get the actual value from that Promise that your function returned.
Your code works because it is not returning promise object and actually waiting for the response. It returns the json at the end. So, execution holds till it gets the response.
If your function is async you don't need to return promise, your return statement will wait for all 'await' statement before it to complete.
Your own code works, because you replaced the usage of .then() with the usage of async await.