I have a function that resolves a Promise without passing any arguments:
const checkUser = (user)=> {
let promise = Parse.Promise();
if(user.success){
promise.resolve();
} else {
promise.reject("Error");
}
}
The problem is that in all tutorials that I read, they assign the returning value to a variable, like this:
let response = await checkUser(user);
In the above case, can I just await for the promise without assigning the result to a variable or this is not recommended? For example:
...
await checkUser(user);
...
Yes, you can totally do that. JavaScript will still wait for the promise to resolve.
Here is a modification of MDN's first example for await. It does not return a value but still waits for the Promise to resolve before running the code after await.
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(resolve, 2000);
});
}
(async function() {
console.log(1)
await resolveAfter2Seconds();
console.log(2);
})()
I think it is a bad practice to resolve a promise with nothing, as that's the appropriate channel for results. And if you don't want to get a result, why would you have called a function at all? This might apply to functional programming mostly, though.
Can I just await the promise without assigning the result to a variable or this is not recommended?
Yes, you can totally do that. If you don't need to do anything with the result - like when you know that it always is undefined - then you can just ignore it.
Related
I know an async function returns a Promise, so I thought of replacing this code:
const hi = function (delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('hi');
resolve();
},delay)
});
};
const bye = async () => {
await hi(1000);
await hi(1000);
await hi(1000);
await hi(1000);
return 'bye';
};
bye().then((msg) => {
console.log(msg);
});
with this code:
const hi = async (delay) => {
setTimeout(() => {
console.log('hi');
},delay);
};
const bye = async () => {
await hi(1000);
await hi(1000);
await hi(1000);
await hi(1000);
return 'bye';
};
bye().then((msg) => {
console.log(msg);
});
The only difference is I'm using an async function instead of a function which returns a Promise, since an async function return a promise. But the second code doesn't work as I expected it too. It just console.logs bye immediately and then after 1s console.logs 'hi' 4 times. Can you please help me understand the reason behind this?
One major difference in both code examples is related to the return value of hi function.
In the first code example, function hi returns a promise whereas in the second code example, function hi does not explicitly returns any value; as a result, the promise returned by the async function is implicitly resolved with the value of undefined without waiting for the setTimeout's callback function to execute.
A promise object doesn't makes anything asynchronous; its just a wrapper around an already asynchronous operation and the promise object notifies whether the asynchronous operation completed successfully or whether it failed.
In the first code example, you have a wrapper promise around the setTimeout. That wrapper promise is not fulfilled until resolve is called from within the callback function of the setTimeout.
In the second code example, the promise returned by the async function isn't automatically bound to the asynchronous code inside the function.
After calling setTimeout, code execution reaches the end of the hi function; As there is no explicit return statement, promise returned by the async function is fulfilled with the value of undefined. This happens before the callback function of the setTimeout is called.
What's confusing you here is the setTimeout, your await hi( will actually await, but there isn't anything to wait for, that promise will almost immediately resolve and return so all 4 of them will execute almost instantaneously, then log "bye", then after 1 second your timeout will trigger and console.log "hi".
This answer adds some additional info to the one provided by Yousaf.
To shortly answer your comment:
Thanks for the explanation. Is there any way to write the hi function without using the 'Promise' keyword?
The answer is no. To convert a function using a callback interface to one that uses a promise you will have to manually create a promise somewhere. Functions using an older interface (usually) don't return promises, therefore you cannot await them. You can only await objects that are thenable (i.e. has a "then" method). If you await a non-thenable value or object, the await statement is essentially ignored.
MDN recommends wrapping callback methods at the lowest level possible:
Creating a Promise around an old callback API
A Promise can be created from scratch using its constructor.
This should be needed only to wrap old APIs.
In an ideal world, all asynchronous functions would already return
promises. Unfortunately, some APIs still expect success and/or failure
callbacks to be passed in the old way. The most obvious example is the
setTimeout() function:
setTimeout(() => saySomething("10 seconds passed"), 10*1000);
Mixing old-style callbacks and promises is problematic. If
saySomething() fails or contains a programming error, nothing
catches it. setTimeout is to blame for this.
Luckily we can wrap setTimeout in a promise. Best practice is to
wrap problematic functions at the lowest possible level, and then
never call them directly again:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(10*1000).then(() => saySomething("10 seconds")).catch(failureCallback);
Basically, the promise constructor takes an executor function that
lets us resolve or reject a promise manually. Since setTimeout()
doesn't really fail, we left out reject in this case.
If you would apply this info to your situation, this means wrapping the setTimeout() call within a Promise constructor like shown above.
// The only responsibility of this function is converting the old
// callback interface, to a promise interface.
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Then use this new function in hi() instead of setTimeout().
async function hi(delay) {
await wait(delay);
console.log('hi');
}
A promise captures the result of an asynchronous processing that just started.
On the version of the code without promises, the asynchronous processing happens the same way as on the first version but there is nobody there waiting for it when it completes.
The second version of the hi() function is synchronous. It completes immediately. The async keyword does not have any effect on it. Also await hi() does not help, the function is still synchronous and it completes immediately.
this is a problem that is going around for DAYS in my team:
We can't figure out how to save the result of a promise (after .then) in a variable.
Code will explain better what I mean:
We start with a simple async function that retrieves the first item of a list:
const helloWorld = () => [{ name: 'hi' }];
async function getFirstHelloWorld() {
const helloWorldList = await helloWorld();
return helloWorldList[0].name;
}
Now, we would like to call this function and save the content of aList:
let aList;
const a = async() => {
aList = await getFirstHelloWorld()
}
a().then(() => {
console.log('got result:', aList)
console.log('aList is promise:', aList instanceof Promise)
});
console.log(aList)
aList called within the .then(), console.logs the right value.
aList called outside the .then(), console.logs Promise { }
How can we save returned values from promises to a variable?
You cannot take an asynchronously retrieved value, stuff it in a higher scoped variable and then try to use it synchronously. The value will not be present yet because the asynchronous result has not been retrieved yet. You're attempting to use the value in the variable before it has been set.
Please remember that await only suspends execution of a local function, it does not stop the caller from running. At the point you do an await, that function is suspended and the async function immediately returns a promise. So, NO amount of await or return gets you anything by a promise out of an async function. The actual value is NEVER returned directly. All async functions return a promise. The caller of that function then must use await or .then() on that promise to get the value.
Try running this code and pay detailed attention to the order of the log statements.
console.log("1");
const helloWorld = () => [{ name: 'hi' }];
async function getFirstHelloWorld() {
const helloWorldList = await helloWorld();
return helloWorldList[0].name;
}
let aList;
const a = async() => {
console.log("beginning of a()");
aList = await getFirstHelloWorld()
console.log("end of a()");
}
console.log("2");
a().then(() => {
console.log('got result:', aList)
console.log('aList is promise:', aList instanceof Promise)
});
console.log("3");
console.log('attempting to use aList value');
console.log(aList)
console.log("4");
That will give you this output:
1
2
beginning of a()
3
attempting to use aList value
undefined
4
end of a()
got result: hi
aList is promise: false
Here you will notice that you are attempting to use the value of aList BEFORE a() has finished running and set the value. You simply can't do that in Javascript asynchronous code, whether you use await or .then().
And, remember that the return value from an async function becomes the resolved value of the promise that all async functions return. The value is not returned directly - even though the code syntax looks that way - that's a unique property of the way async functions work.
Instead, you MUST use the asynchronous value inside the .then() where you know the value is available or immediately after the await where you know the value is available. If you want to return it back to a caller, then you can return it from an async function, but the caller will have to use .then() or await to get the value out of the promise returned from the async function.
Assigning an asynchronously retrieved value to a higher scoped variable is nearly always a programming error in Javascript because nobody wanting to use that higher scoped variable will have any idea when the value is actually valid. Only code within the promise chain knows when the value is actually there.
Here are some other references on the topic:
Why do I need to await an async function when it is not supposedly returning a Promise?
Will async/await block a thread node.js
How to wait for a JavaScript Promise to resolve before resuming function?
Using resolved promise data synchronously
How to Write Your Code
So, hopefully it is clear that you cannot escape an asynchronous result. It can only be used in asynchronous-aware code. You cannot turn an asynchronously retrieved result into something you can use synchronously and you usually should NOT be stuffing an asynchronous result into a higher scoped variable because that will tempt people writing code in this module to attempt to use the variable BEFORE is is available. So, as such, you have to use the value inside a .then() handler who's resolved value has the value you want or after an await in the same function where the await is. No amount of nesting in more async functions let you escape this!
I've heard some people refer to this as asynchronous poison. Any asynchronous operation/value anywhere in a flow of code makes the entire thing asynchronous.
Here's a simplified version of your code that shows returning the value from an async function which will make it the resolved value of the promise the async function returns and then shows using .then() on that promise to get access to the actual value. It also shows using .catch() to catch any errors (which you shouldn't forget).
FYI, since this is not real asynchronous code (it's just synchronous stuff you've wrapped in some promises), it's hard to tell what the real end code should be. We would need to see where the actual asynchronous operation is rather than just this test code.
const helloWorld = () => [{ name: 'hi' }];
async function getFirstHelloWorld() {
const helloWorldList = await helloWorld();
return helloWorldList[0].name;
}
getFirstHelloWorld().then(name => {
// use the name value here
console.log(name);
}).catch(err => {
console.log(err);
});
You need to return the variable like this and use it within the .then callback.
const a = async() => {
const res = await getFirstHelloWorld()
return res
}
a().then((data) => {
console.log('got result:', data)
});
I work on create PDF with pdfmake and i get images of many charts with html2canvas.
How i can get the value of the Promise of html2canvas return?
CODE
var img = { token: html2canvas(document.getElementById("chartContainer")).then(canvas => {
return canvas.toDataURL("image/jpeg,1.0")
}).then(canvas =>{return canvas})}
console.log (img.token); // Promise { <state>: "pending" }
alert(img.token); // Object Promise
I want to use images outside the function.
Thanks for your help!
You have to wait for the promise to complete. When the promise completes, the function you passed into .then(/*...*/) is called.
You also don't need to use an additional .then after toDataURL, because toDataURL runs synchronously (it doesn't return a promise). You only need to use .then for functions that return a promise.
So you can rewrite it like this instead:
html2canvas(document.getElementById("chartContainer"))
.then(canvas => {
// This code will run once the promise has completed
var img = { token: canvas.toDataURL("image/jpeg,1.0") };
console.log(img.token);
alert(img.token);
});
If promises and .then are confusing, and you are somehow trying to get values out of the future to use in the here and now, consider async/await, which allows you to write asynchronous code in a way which sorta kinda looks synchronous:
const canvas = await html2canvas(document.getElementById("chartContainer"));
const token = canvas.toDataURL("image/jpeg,1.0");
const img = { token };
console.log (img.token);
alert(img.token);
Internally, the above code is transpiled or interpreted in such as way as to wait for the html2canvas promise to fulfill, using the equivalent of .then. In other words, it ends up looking like this:
html2canvas(document.getElementById("chartContainer"))
.then(canvas => {
const token = canvas.toDataURL("image/jpeg,1.0");
const img = { token };
console.log (img.token);
alert(img.token);
});
Or, if you don't want to use async/await for some reason, you could just write it like this to start with.
PS. Assuming the code above is inside a function, it would need to be an async function.
I am learning about Javascript ES2017 async/await feature.
Been reading a lot about it, and came across an understanding that await is like yield, and allows us to wait for the promises to complete.
From https://javascript.info/async-await
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();
The question I have is, can I add await to every single line of code?
Or what happens if I remove the word await?
And another question is, if async/ await makes the code seems to run synchronously and in order, why don't we don't use it at all (means make everything stay synchronous in the first place?)
Thank you!
async functions are just syntactic sugar around Promises. It does nothing to change the function to be synchronous. In fact any function that is async implicitly returns a Promise object.
All await does is provide a convenient way to wait for a Promise. If you remove the await keyword, then the Promise will not be "unwrapped," which is not what you want if your function is going to operate on the result of that Promise.
To illustrate, this is the desugared version of your async function:
function showAvatar() {
let githubUser;
// read our JSON
return fetch('/article/promise-chaining/user.json').then(response => {
return response.json();
}).then(user => {
// read github user
return fetch(`https://api.github.com/users/${user.name}`);
}).then(githubResponse => {
return githubResponse.json();
}).then(user => {
githubUser = user;
// 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
return new Promise((resolve, reject) => setTimeout(resolve, 3000));
}).then(() => {
img.remove();
return githubUser;
});
}
So await essentially just adds a .then callback to a Promise. Without await being specified, you'll just have a Promise object, not the result of the Promise.
await tells the runtime to wait for the promise returned from the expression on the right-hand side to be fulfilled.
In the following code control is stopped in the async function until the promise returned by the fetch invocation is fulfilled, and the response value sent to the fulfilled promise is printed to the console.
async function go() {
let response = await fetch('http://www.example.com');
console.log(response); // print the response after some time
}
go();
If the await keyword was omitted then control would immediately continue to the next line of the function and the the promise returned by fetch would be immediately printed to the console.
async function go() {
let response = fetch('http://www.example.com');
console.log(response); // print the promise from fetch immediately
}
go();
await is a contextual keyword that only means something special when used inside a function marked async. By marking a function async you are telling the runtime to:
treat the function as a generator function
yield a value where the user types await (usually a promise)
wrap the function in special handling logic so that progress through the function only occurs when each promise yielded by await is fulfilled
In this way, asynchronous control flows can be written in a style closer to the traditional synchronous style. ie. without nesting, callbacks or visible promises. try..catch can also be used in the normal way.
As another answer mentions, async, and await are syntactic sugar that ask the runtime to use existing objects (generator functions and Promises) behind the scenes to make async code easier to read and write.
can you await every line
I would guess you can. If the expression on the right of the await results in a promise, then the async/await behaviour detailed above occurs.
If the expression on the right of the await does not result in a promise, then I would guess the value is wrapped in a resolved promise for you, and logic continues per the above, as though the value came from an immediately resolved promise. This is a guess.
First, async / await, work with promises. If you are not calling from an async function or the await sentence is not then compatible, it won't work. In most cases if you remove await, you are going to end with a Promise and not the value of the promise.
Secondly, there are some cases that you may want to continue with the execution of the code while you are resolving some asynchronous task.
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