This code snippet is taken from google developers:
// map some URLs to json-promises
const jsonPromises = urls.map(async url => {
const response = await fetch(url);
return response.json();
});
I understand that at the end jsonPromises is going to be an array of Promises. However, i'm not completely sure how that happens.
According to my understanding, when execution gets to this line const response = await fetch(url); it moves it from call stack to Web Api, does the same to return response.json(); and moves to the next url from urls. Is it correct ? I understand how event loop works when it comes to simple setTimeout , but this example confuses me a lot.
Here's what happens:
1) The .map function runs, and executes the callback for each element (the async function). The function calls fetch, which starts some under the hood magic inside the engine. fetch returns a Promise. The await gets reached, which halts the functions execution. The call to the function done by .map evaluates to a promise. .map collects all those promises and stores them in an array and returns the array.
2) Somewhen, one of the fetches running under the hood has etablished a connection, and calls back into JS and resolves the fetch promise. That causes the async function call to go on. res.json() gets called, which causes the engine to collect all the packets from the connection and parse it as JSON under the hood. That again returns a Promise, which gets awaited, which causes the execution to stop again.
3) Somewhen, one of the connections end, and the whole response is available as JSON. The promise resolves, the async function call continues execution, returns, which causes the promise (the one in the array) to be resolved.
Since async functions return a promise, think of the example as any other promise-returning function:
// map happens synchronously and doesn't wait for anything
const map = (arr, cb) => {
const mapped = [];
for (const [i, element] of arr.entries()) {
mapped.push(cb(element))
}
return mapped;
}
// just return a promise that resolves in 5 seconds with the uppercased string
const promiseFn = url => {
return new Promise(resolve => setTimeout(() => resolve(url.toUpperCase()), 5000))
}
const promiseArray = map(['url', 'url', 'url'], promiseFn)
All the promises returned by the async callback are pushed synchronously. Their states are modified asynchronously.
If on the other hand you're looking for the difference between setTimeout and a Promise vis-a-vis the event loop, check the accepted answer here: What is the relationship between event loop and Promise
Related
I have the following endpoint in a class called UserApi.js:
const controller = 'User';
...
export async function getEmployeeInfo(employeeId)
{
const query = createQueryFromObject({employeId});
const response = await get(`/${controller}/EmployeeInfo?${query}`);
return retrieveResponseData(response, []);
}
This is going to get the required information from an action method in the backend of UserController.cs.
Now, say that I want to display this information in EmployeeView.vue class, do I have to await it again? Why or why not? Initially, I would say no, you don't, as you already dealt with the await/async in the UserApi.js class, but what about the Promise.resolve? Please explain.
methods: {
async setReportData(
employeeId
) {
this.isBusy = true;
Promise.resolve(getEmployeeInfo(
employeeId
)).then((resultsEmployeeInfo) => {
this.reportDatatableEmployeeInfo = resultsEmployeeInfo;
})
.catch(() => {
this.alerts.error('An error has occurred while fetching the data');
})
.finally(() => {
this.isBusy = false;
});
},
Update:
....
* #param {Object} response
* #param {any} defaultData
* #param {Function} predicate
* #returns {Promise}
*/
export function retrieveResponseData(response, defaultData = null, predicate = (predicateResponse) => predicateResponse) {
const data = predicate(response) ? response.data : null;
return data || defaultData;
}
You need to await it since a function declared with async keyword ALWAYS returns a Promise, even if you do only synchronous stuff inside of it, hence you need to await or "thenize" it to access the value it resolved to. That depends from the implementation details of async functions which are just generators that yield promises.
If this concerns you because you work inside sync modules and don't like to use async functions just to execute more async functions, there's a good news, TOP-LEVEL await MODULES proposal is at stage 4, so it'll very soon be shipped with the next ECMA version. This way you will be able to await inside modules as if they were wrapped by async functions !
https://github.com/tc39/proposal-top-level-await
I can't tell if you need to await it again, because I can't tell what retrieveResponseData does. It might take the resolved value and wrap it in a fresh promise, which would then require callers of getEmployeeInfo to await the result.
Here's the why:
A Promise is a wrapper around a value
await unwraps a Promise. So does the .then() handler you can register with a Promise (but the value is only unwrapped within the function you provide to .then()).
Just like a gift in the real world, once something has been unwrapped, you don't need to unwrap it again. However, very conveniently for us, you can still use await on a value that is not wrapped in a Promise, and it will just give you the value.
You can wrap any value in a Promise, like so:
let wrappedFive = Promise.resolve(5)
//> wrappedFive is a Promise that must be unwrapped to access the 5 inside it
// this does _exactly_ the same thing as the above
let wrappedFive = new Promise(resolve => {
resolve(5)
})
Sometimes you end up in a situation where you can't use await, because you're in a function that cannot be marked async. The lifecycle methods of front-end frameworks like React (and possibly Vue) are like that: the framework needs each lifecycle method to do its job and be done immediately. If you mark the lifecycle method as async, you can often prevent it from having the intended effect.
In cases like that, you often need to use chained .then() handlers, which is a little uglier, but it works:
componentDidMount() {
// this API call is triggered immediately by lifecycle,
// but it basically starts a separate thread -- the rest
// of this function does not wait for the call to finish
API.getUserInfo()
.then(userInfo => {
// this happens after the API call finishes, but
// componentDidMount has already finished, so what happens
// in here cannot affect that function
this.setState({ username: userInfo.username })
})
// this happens immediately after the API call is triggered,
// even if the call takes 30 seconds
return 5
}
Note that a Promise does not actually start a separate thread -- these all happen in the same thread that executes the lifecycle method, i.e. the browser's renderer thread. But if you think of the codepath that executes, a Promise that you don't wait for basically introduces a fork into that codepath: one path is followed immediately, and the other path is followed whenever the Promise resolves. Since browserland is pretty much a single-threaded context, it doesn't really hurt you to think of creating a Promise as spawning a separate thread. This is a nuance you can ignore until you are comfortable with asychronous patterns in JS.
Update: retrieveResponseData does not appear to return a Promise. I could be wrong, if predict returns a Promise, but if that were true, then the ternary would always return response.data because unwrapped Promises are truthy values (even Promise.resolve(false) is truthy).
However, anyone who calls getEmployeeInfo will have to wait it, because that function is marked async, and that means it returns a Promise even if nothing inside it is is asynchronous. Consider this extreme example:
// this function returns a number
function gimmeFive() {
return 5
}
// this function returns a Promise wrapped around a number
async function gimmeFive() {
return 5
}
Async function getEmployeeInfo awaits the result of the get call in order to return the value returned by a call to retrieveResponeData.
Assuming neither get nor retrieveResponeData errors, the value syntactically returned in the body of getEmployeeInfo is used to resolve the promise object actually returned by calling getEmployeeInfo.
Promise.resolve is not needed to convert the result of calling getEmployeeInfo into a promise because, given async functions return promises, it already is.
It doesn't matter if retrieveResponseData returns a promise or not: standard async function processing waits for a returned promise value to be settled before resolving the promise returned when calling the async function.
Async function setRreportData is declared as async but written using chained promise handler methods to process data and error conditions in order - the async declaration could be omitted, but may be useful if modifications are made.
The results can only be used to update the page at a time when the data has finished being obtained and extracted, shown as a comment in the following code:
setReportData( employeeId) {
this.isBusy = true;
getEmployeeInfo(
employeeId
).then((resultsEmployeeInfo) => {
this.reportDatatableEmployeeInfo = resultsEmployeeInfo;
// At this point the result in this.reportDatatableEmployeeInfo can be used to update the page
})
.catch(() => {
this.alerts.error('An error has occurred while fetching the data');
})
.finally(() => {
this.isBusy = false;
});
},
Displaying the data using EmployeeView.vue class must wait until the data is available. The simplest place to insert updating the page (in the posted code) is within the then handler function inside setReportData.
Displaying the data
As posted setReportData does not notify its caller of when report data is available, either by means of a callback or by returning a promise. All it does is save the result in this.reportDatatableEmployeeInfo and dies.
Without using callbacks, setReportData is left with two choices
Return a promise that is fulfilled with, say,resultsEmployeeInfo or undefined if an error occurs:
setReportData( employeeId) {
this.isBusy = true;
// return the final promise:
return getEmployeeInfo(
employeeId
)
.then( (resultsEmployeeInfo) =>
(this.reportDatatableEmployeeInfo = resultsEmployeeInfo)
)
.catch(() => {
this.alerts.error('An error has occurred while fetching the data');
// return undefined
})
.finally(() => {
this.isBusy = false;
});
},
which could be used in a calling sequence using promises similar to
if(!this.busy) {
this.setReportData(someId).then( data => {
if( data) {
// update page
}
}
If you wanted to make the call in an async context you could use await:
if(!this.busy) {
const data = await this.setReportData(someId);
if( data) {
// update page
}
}
Update the page from within setReportData after the data becomes available ( as shown as a comment in the first part of this answer). The method should probably be renamed from setReportData to getReportData or similar to reflect its purpose.
I've researched many many posts and articles, and documentation. I'm seeking deeper understanding.
From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
The await expression causes async function execution to pause until a Promise is settled, [...]
let fetchLocation = () => {
//fetches information from satellite system and
//returns a Vector2 of lat long on earth
return new Promise(resolve => {
setTimeout(() => {
resolve({
lat: 73,
long: -24
});
}, 2000);
});
}
//async functions automatically return a promise.
let main = async() => {
//awaits the promises resolution
let result = await fetchLocation();
console.log("I'm the awaited promise result", result)
//immediately returns a promise as promise pending
return result;
}
console.log(main().then(res => console.log("I'm the original promise, just passed along by the async function, it didn 't a-wait for me", res)))
Before going down this rabbit hole, I knew async/await was syntactical sugar to make async code look synchronous.
I therefore fully expected to see main return the lat long coordinates. Instead it returns a promise.
A couple questions.
Is the MDN documentation wrong? Does it not actually pause execution? It seems that the await keyword does not actually "pause" function execution... invoking main immediately returns a Promise<pending> object. However, after it returns we receive the value of the fulfilled promise in the awaited variable result. I suppose this came from the event loop? (And an async return statement is still synchronous.)
So it seems that await unwraps a promise, and async wraps a promise, but you need to use await within async. I get that await might actually be promise.then() underneath the hood but if they want to make it fully synchronous in appearance, why not have the async function return the resolved promise value when used with await? Otherwise async await seems a bit redundant.
Is the MDN documentation wrong?
No
Does it not actually pause execution?
It pauses the execution of the async function, hands a Promise back to the calling function — because it hasn't reached the point in the async function where it knows what to return — then the execution of the calling function (and the rest of the code) continues.
However, after it returns we receive the value of the fulfilled promise in the awaited variable result. I suppose this came from the event loop?
When the event loop stops being busy (running the code that called the async function in the first place and anything else that it picks up in the meantime) and the promise that was being awaited resolves, then the async function is woken up and given the resolved value of the function to continue processing.
why not have the async function return the resolved promise value when used with await?
Because that would block the entire event loop, which is why asynchronous logic (starting with callback style APIs) was introduced into JS in the first place.
Otherwise async await seems a bit redundant.
Consider a situation where you want to request a number of URLs from an API serially (so that you don't shove too many simultaneous requests at the API):
const data = [];
for (let i = 0; i < urls.length; i++) {
const response = await fetch(urls[i]);
const response_data = await response.json();
data.push(response_data);
}
If you were to rewrite that without await then you'd probably end up with something recursive to count your way through the loop. It would be significantly more complex.
async/await makes dealing with complex combinations of promises simple, and simple promises trivial.
knew async/await was syntactical sugar to make async code look synchronous.
And it does that inside the async function and only inside it.
It doesn't really pause the function, it just allows you to write the rest of the function code as if it were. But it's really just wrapping the rest of the code of the function into a .then().
E.g.
await foo = someFunc();
console.log(foo);
is executed like
someFunc().then((foo => {
console.log(foo);
});
Since it's really still asynchronous, you can't return the resolved value, because the original calling function returns immediately. But if the calling function is also declared async, it returns a Promise, and the caller can either use await again (so it appears to be synchronous) or use .then().
I have some functions,
const firstResult = await firstFunction(parameters)
const secondResult = await secondFunction(firstResult)
const finalResult = await finalFunction(secondResult)
They're asynchronous functions, returning some promise. Now there's a scenario like I have an array of parameters that is getting passed to `firstFunction at each iteration and it should follow the same flow of execution. Something like this:
const results = arrayOfParamters.map(async parameter => {
const firstResult = await firstFunction(parameters)
const secondResult = await secondFunction(firstResult)
const finalResult = await finalFunction(secondResult)
return finalResult
})
So how can I achieve this more efficiently? One of the efficient solutions that I can visualize is to execute firstFunction for the first parameter, start executing secondFunction and till then the promise for secondFunction is fulfilled, start executing the firstFunction for the next iteration of arrayOfParameters. Is there any way I can achieve this? Seems like Promise.all will not be worth it if the whole loop is going to be executed synchronously.
Your code is already set up to do that.
It's going to start by running the map code for element 0 of the array. It synchronously calls firstFunction but then it hits an await, and so the mapping function returns a promise. This promise gets put in element 0 of results. Then it does the same thing for element 1, then 2, etc. So it will call firstFunction for every element of the array, and assign an array of promises to results, but none of them will block or wait.
The array of promises has now been created, so the code continues on to whatever comes after this line. Over time, the calls to firstFunction will start finishing, and the async functions will resume, calling secondFunction for each element of the array (not necessarily in exactly the same order, since this is happening at an unpredictable time). And then eventually they'll all do third function, and finally they'll resolve their promises to finalResult.
So the only thing missing from your code is to know when it's all done, and for that you need Promise.all:
const promises = arrayOfParamters.map(async parameter => {
const firstResult = await firstFunction(parameter)
const secondResult = await secondFunction(firstResult)
const finalResult = await finalFunction(secondResult)
return finalResult
})
const results = await Promise.all(promises)
Because Promise.then chain the result as a parameter to the next function you can use that instead of async await
You can use Promise.all like this:
const promiseChain = arrayOfParamters.map((parameter) => firstFunction(parameter).then(secondFunction).then(finalFunction));
const results = await Promise.all(promiseChain);
I am new to JS and was learning promises. So, let's say we have this code:
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); // (*)
}).then(function(result) { // (**)
alert(result); // 1
return result * 2;
})
As you can see the code above, when promise is invoked, setTimeout is run via callback queue. The question is When setTimeOut is sent to a browser, will JS engine omit .then() and continues running the rest of the code until the promise resolves? Secondly, async/await example:
async function showAvatar() {
// read our JSON
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
// read github user
let githubResponse = await fetch(`https://api.github.com/users/${user.name}`);
let githubUser = await githubResponse.json();
// show the avatar
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
// wait 3 seconds
await new Promise((resolve, reject) => setTimeout(resolve, 3000));
img.remove();
return githubUser;
}
showAvatar();
When showAvatar function is called, JS engine will encounter let response = await fetch('/article/promise-chaining/user.json'); and sends fetch to the browser to handle. The second question is Will JS engine wait until fetch gets resolved or Will JS engine continue executing let user = await response.json(); and the rest of the code inside showAvatar function? If so, how can JS engine handle response.json() since response is not received? Hope you got my point))).
Your first example works like this:
new Promise runs, calling the function you pass it (the executor function) synchronously
Code in the executor function calls setTimeout, passing in a function to call 1000ms later; the browser adds that to its list of pending timer callbacks
new Promise returns the promise
then is called, adding the function you pass into it to the promise's list of fulfillment handlers and creating a new promise (which your code doesn't use, so it gets thrown away).
1000ms or so later, the browser queues a call to the setTimeout callback, which the JavaScript engine picks up and runs
The callback calls the resolve function to fulfill the promise with the value 1
That triggers the promise's fulfillment handlers (asynchronously, but it doesn't really matter for this example), so the handler attached in Step 4 gets called, showing the alert and then returning result * 2 (which is 1 * 2, which is 1). That value is used to fulfill the promise created and thrown away in Step 4.
Will JS engine wait until fetch gets resolved or Will JS engine continue executing let user = await response.json();...
It waits. The async function is suspended at the await in await fetch(/*...*/), waiting for the promise fetch returned to settle. While it's suspended, the main JavaScript thread can do other things. Later, when the promise settles, the function is resumed and either the fulfillment value is assigned to response (if the promise is fulfilled) or an exception will get thrown (if it is rejected).
More generally: async functions are synchronous up until the first await or return in their code. At that point, they return their promise, which is settled later based on the remainder of the async function's code.
In a comment you asked:
when async function is suspended at each await, will async function is removed from the call stack and is put back to the call stack again when the promise being awaited settles?
At a low level, yes; but to make debugging easier, a good, up-to-date JavaScript engine maintains an "async call stack" they use for error traces and such. For instance, if you run this on Chrome...
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function inner() {
await delay(80);
throw new Error("boom");
}
async function outer() {
await inner();
}
function wrapper() {
outer()
.then(result => {
console.log(result);
})
.catch(error => {
console.log(error.stack);
});
}
wrapper();
...the stack looks like this:
Error: boom
at inner (https://stacksnippets.net/js:18:11)
at async outer (https://stacksnippets.net/js:22:5)
Notice the "async" prior to "outer," and also notice that wrapper isn't mentioned anywhere. wrapper is done and has returned, but the async functions were suspended and resumed.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I have an es6 class, with an init() method responsible for fetching data, transforming it, then update the class's property this.data with newly transformed data.
So far so good.
The class itself has another getPostById() method, to just do what it sounds like. Here is the code for the class:
class Posts {
constructor(url) {
this.ready = false
this.data = {}
this.url = url
}
async init() {
try {
let res = await fetch( this.url )
if (res.ok) {
let data = await res.json()
// Do bunch of transformation stuff here
this.data = data
this.ready = true
return data
}
}
catch (e) {
console.log(e)
}
}
getPostById(id){
return this.data.find( p => p.id === id )
}
}
Straightforward, except I have an async/await mechanism in the init() method.
Now, this code will work correctly:
let allPosts = new Posts('https://jsonplaceholder.typicode.com/posts')
allPosts.init()
.then( d => console.log(allPosts.getPostById(4)) )
// resulting Object correctly logged in console
but it only gets printed into the console:
How could I use allPosts.getPostById(4) as a return of a function ?
Like:
let myFunc = async () => {
const postId = 4
await allPosts.init() // I need to wait for this to finish before returning
// This is logging correct value
console.log( 'logging: ' + JSON.stringify(allPosts.getPostById( postId ), null, 4) )
// How can I return the RESULT of allPosts.getPostById( postId ) ???
return allPosts.getPostById( postId )
}
myFunc() returns a Promise but not the final value. I have read several related posts on the subject but they all give example of logging, never returning.
Here is a fiddle that includes two ways of handling init(): using Promise and using async/await. No matter what I try, I can't manage to USE the FINAL VALUE of getPostById(id).
The question of this post is: how can I create a function that will RETURN the VALUE of getPostById(id) ?
EDIT:
A lot of good answers trying to explain what Promises are in regards to the main execution loop.
After a lot of videos and other good reads, here is what I understand now:
my function init() correctly returns. However, within the main event loop: it returns a Promise, then it is my job to catch the result of this Promise from within a kinda parallel loop (not a new real thread). In order to catch the result from the parallel loop there are two ways:
use .then( value => doSomethingWithMy(value) )
use let value = await myAsyncFn(). Now here is the foolish hiccup:
await can only be used within an async function :p
thus itself returning a Promise, usable with await which should be embed in an async function, which will be usable with await etc...
This means we cannot really WAIT for a Promise: instead we should catch parallel loop indefinitely: using .then() or async/await.
Thanks for the help !
As for your comment; I'll add it as answer.
The code you write in JavaScript is run on one thread, that means that if your code could actually wait for something it will block any of your other code from getting executed. The event loop of JavaScript is explained very well in this video and if you like to read in this page.
A good example of blocking code in the browser is alert("cannot do anything until you click ok");. Alert blocks everything, the user can't even scroll or click on anything in the page and your code also blocks from executing.
Promise.resolve(22)
.then(x=>alert("blocking")||"Hello World")
.then(
x=>console.log(
"does not resolve untill you click ok on the alert:",
x
)
);
Run that in a console and you see what I mean by blocking.
This creates a problem when you want to do something that takes time. In other frameworks you'd use a thread or processes but there is no such thing in JavaScript (technically there is with web worker and fork in node but that's another story and usually far more complicated than using async api's).
So when you want to make a http request you can use fetch but fetch takes some time to finish and your function should not block (has to return something as fast as possible). This is why fetch returns a promise.
Note that fetch is implemented by browser/node and does run in another thread, only code you write runs in one thread so starting a lot of promises that only run code you write will not speed up anything but calling native async api's in parallel will.
Before promises async code used callbacks or would return an observable object (like XmlHttpRequest) but let's cover promises since you can convert the more traditional code to a promise anyway.
A promise is an object that has a then function (and a bunch of stuff that is sugar for then but does the same), this function takes 2 parameters.
Resolve handler: A function that will be called by the promise when the promise resolves (has no errors and is finished). The function will be passed one argument with the resolve value (for http requests this usually is the response).
Reject handler: A function that will be called by the promise when the promise rejects (has an error). This function will be passed one argument, this is usually the error or reason for rejection (can be a string, number or anything).
Converting callback to promise.
The traditional api's (especially nodejs api's) use callbacks:
traditionalApi(
arg
,function callback(err,value){
err ? handleFail(err) : processValue(value);
}
);
This makes it difficult for the programmer to catch errors or handle the return value in a linear way (from top to bottom). It gets even more impossible to try and do things parallel or throttled parallel with error handling (impossible to read).
You can convert traditional api's to promises with new Promise
const apiAsPromise = arg =>
new Promise(
(resolve,reject)=>
traditionalApi(
arg,
(err,val) => (err) ? reject(err) : resolve(val)
)
)
async await
This is what's called syntax sugar for promises. It makes promise consuming functions look more traditional and easier to read. That is if you like to write traditional code, I would argue that composing small functions is much easier to read. For example, can you guess what this does?:
const handleSearch = search =>
compose([
showLoading,
makeSearchRequest,
processRespose,
hideLoading
])(search)
.then(
undefined,//don't care about the resolve
compose([
showError,
hideLoading
])
);
Anayway; enough ranting. The important part is to understand that async await doesn't actually start another thread, async functions always return a promise and await doesn't actually block or wait. It's syntax sugar for someFn().then(result=>...,error=>...) and looks like:
async someMethod = () =>
//syntax sugar for:
//return someFn().then(result=>...,error=>...)
try{
const result = await someFn();
...
}catch(error){
...
}
}
The examples allways show try catch but you don't need to do that, for example:
var alwaysReject = async () => { throw "Always returns rejected promise"; };
alwaysReject()
.then(
x=>console.log("never happens, doesn't resolve")
,err=>console.warn("got rejected:",err)
);
Any error thrown or await returning a rejected promise will cause the async function to return a rejected promise (unless you try and catch it). Many times it is desirable to just let it fail and have the caller handle errors.
Catching errors could be needed when you want the promise to succeed with a special value for rejected promises so you can handle it later but the promise does not technically reject so will always resolve.
An example is Promise.all, this takes an array of promises and returns a new promise that resolves to an array of resolved values or reject when any one of them rejects. You may just want to get the results of all promises back and filter out the rejected ones:
const Fail = function(details){this.details=details;},
isFail = item => (item && item.constructor)===Fail;
Promise.all(
urls.map(//map array of urls to array of promises that don't reject
url =>
fetch(url)
.then(
undefined,//do not handle resolve yet
//when you handle the reject this ".then" will return
// a promise that RESOLVES to the value returned below (new Fail([url,err]))
err=>new Fail([url,err])
)
)
)
.then(
responses => {
console.log("failed requests:");
console.log(
responses.filter(//only Fail type
isFail
)
);
console.log("resolved requests:");
console.log(
responses.filter(//anything not Fail type
response=>!isFail(response)
)
);
}
);
Your question and the comments suggest you could use a little intuition nudge about the way the event loop works. It really is confusing at first, but after a while it becomes second nature.
Rather than thinking about the FINAL VALUE, think about the fact that you have a single thread and you can't stop it — so you want the FUTURE VALUE -- the value on the next or some future event loop. Everything you write that is not asynchronous is going to happen almost immediately — functions return with some value or undefined immediately. There's nothing you can do about. When you need something asynchronously, you need to setup a system that is ready to deal with the async values when they return sometime in the future. This is what events, callbacks, promises (and async/await) all try to help with. If some data is asynchronous, you simply can not use it in the same event loop.
So what do you do?
If you want a pattern where you create an instance, call init() and then some function that further process it, you simply need to setup a system that does the processing when the data arrives. There are a lot of ways to do this. Here's one way that's a variation on your class:
function someAsync() {
console.log("someAsync called")
return new Promise(resolve => {
setTimeout(() => resolve(Math.random()), 1000)
})
}
class Posts {
constructor(url) {
this.ready = false
this.data = "uninitilized"
this.url = url
}
init() {
this.data = someAsync()
}
time100() {
// it's important to return the promise here
return this.data.then(d => d * 100)
}
}
let p = new Posts()
p.init()
processData(p)
// called twice to illustrate point
processData(p)
async function processData(posts) {
let p = await posts.time100()
console.log("randomin * 100:", p)
}
init() saves the promise returned from someAsync(). someAsync() could be anything that returns a promise. It saves the promise in an instance property. Now you can call then() or use async/await to get the value. It will either immediately return the value if the promise has already resolved or it will deal with it when it has resolved. I called processData(p) twice just to illustrate that it doesn't calle the someAsync() twice.
That's just one pattern. There are a lot more — using events, observables, just using then() directly, or even callbacks which are unfashionable, but still can be useful.
NOTE: Wherever you use await it has to be inside an async function.
Check out the UPDATED FIDDLE
You need to use await myFunc() to get the value you expect from getPostById because an async function always returns a promise.
This sometimes is very frustrating as the whole chain needs to be converted into async functions but that's the price you pay for converting it to a synchronous code, I guess. I am not sure if that can be avoided but am interested in hearing from people who have more experience on this.
Try out the below code in your console by copying over the functions and then accessing final and await final.
NOTE:
An async function CAN contain an await expression.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
There is no rule that is must have await in order to even declare an async function.
The example below uses an async function without await just to show that an async function always returns a promise.
const sample = async () => {
return 100;
}
// sample() WILL RETURN A PROMISE AND NOT 100
// await sample() WILL RETURN 100
const init = async (num) => {
return new Promise((resolve, reject) => {
resolve(num);
});
}
const myFunc = async (num) => {
const k = await init(num);
return k;
}
// const final = myFunc();
// final; This returns a promise
// await final; This returns the number you provided to myFunc