How to call an async function inside an if statement? - javascript

To keep variables in background.js in chrome extension I need to reinitiate some global variables, and I meet some difficulties.
Here is the code(fiddle) that I want to use to illustrate the problem:
var temp = null;
function someTimeConsumingThing() {
return new Promise(function(resolve,reject) {
setTimeout(resolve, 2000);
temp = 10;
})
}
async function a(){
if(temp==null){
await someTimeConsumingThing();
}
return temp
}
function b(){
let localTemp = a();
console.log(localTemp);
}
b();
In the above code snippet, the temp variable would sometimes be null and to ensure that temp is not null I should call an async function someTimeConsumingThing. As we can see, the console.log outputs a Promise rather than 10; and an error would occur if I add await before a():
Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules"
How can I tackle this problem? I have read many related but unhelpful answers here and I don't know how to optimize my search input to get the most related question. This problem would be very naive for JS experts and there are certainly available answers on this site.

You forgot to use await when calling a(), which means you printed the returned promise instead of the async result of a(). The containing function therefore also needs to be async, since it contains an await call.
Note that it's good practice to mark the someTimeConsumingThing function as async, because it returns a Promise.
Also note that your someTimeConsumingThing function sets temp to 10 immediately, and then delays until returning. I've rewritten it so it only sets temp to 10 after the delay has completed.
let temp = null;
async function someTimeConsumingThing() {
return new Promise(resolve => {
setTimeout(()=>{
temp = 10;
resolve();
}, 2000);
})
}
async function a(){
if(temp==null){
await someTimeConsumingThing();
}
return temp;
}
async function b(){
let localTemp = await a();
console.log(localTemp);
}
b();

A function that calls an async function and needs the result of that function, must in itself (by definition) be asynchronous.
Just change b so that it is an async function:
async function b(){
let localTemp = await a();
console.log(localTemp);
}

Related

Losing object reference in Javascript with await

I was learning the event loop of Js and tried to run this function:
async function foo() {
console.log('FIrst');
let a = await new Promise((resolve,reject)=>{
console.log("inside Promise");
resolve();
})
console.log(a);
console.log('Second');
}
foo();
console.log('Three');
On running this code gave output as follows:
FIrst
inside Promise
Three
undefined
Second
I am not able to understand why did a lose its value (the object assignment). I have searched for this behavior but was not able to understand from the answers.
Can someone please explain what is the order of execution which results in this output.
When you await the newly created promise, you're not assigning the promise to a, you're assigning the resolve value of it.
async function foo() {
let a = await new Promise((resolve, reject) => {
// This is where the value stems from
resolve(42);
});
console.log(a);
}
foo();
If you want to keep the await while also retrieving the reference, you can separate those two steps:
async function foo() {
let a = new Promise((resolve, reject) => {
resolve(42);
});
await a;
console.log(a instanceof Promise);
}
foo();
This is because if you are using await it returns to a variable the thing that you've passed in resolve, so you didn't pass any value in resole this is why it is undefined
The reason why the promise's value is undefined is because you did not set the value that the promise should output. If you want the promise to return something other than undefined, place an expression or string inside the resolve() function. If you want the promise to return "Promise completed" after it resolves and console.log the result, simply use the code
async function foo() {
let a = await new Promise((resolve,reject)=>{
resolve("Promise completed");
})
console.log(a);
}
foo();

await with overloaded methods

I have an ES6 class hierarchy. All of the classes in the hierarchy implement a draw() method. Some implement it synchronously but some need this method to be async.
How can I call draw() correctly in this sort of setup?
Is it possible to find if the method being called is async and only then call await on it's result?
You could check if the draw() has a .then function since all promises will have to implement that. If it does, use async, else execute directly
Check this answer on how to determine a promise How do I tell if an object is a Promise?
if(draw.then) { // all async await functions wrap the return into promises. So, this should
const resolvedData = await draw(); // store the data if you need to get the value
else
draw()
I wanted to avoid having to decla(r)e 100 methods as async for the
sake of just the two that actually need it.
A function doesn't have to be async (ie: return a promise) to be awaited. A synchronous function can be awaited too. This means that if a promise is not returned by draw() (ie: your draw() method is synchronous), then the await will convert the returned value of draw() to a resolved promise which it then waits for and retrieves its value.
So, something like the following is possible:
function foo() { // returns a string
return "foo";
}
async function bar() { // implicitly returns a promise
return "bar";
}
(async function() {
const fooStr = await foo(); // foo() is synchronous, but can be `await`ed still
const barStr = await bar(); // bar() is asynchronous, and can be `await`ed
console.log(fooStr, barStr);
})();
With this in mind, you don't need to know if draw() is synchronous or not, instead, you can always await the draw() method, regardless of its return type.
You can check your function if it is async function by using Object.prototype.constructor.name.
var object = {
draw: async function() {}
}
var object1 = {
draw: function() {}
}
console.log(object.draw.constructor.name);
console.log(object1.draw.constructor.name);

Does the asyc\await syntax resolve chained functions?

In this example, if the function was run, would both promises get resolved before foo is return ?
async function() {
var foo = await iReturnAPromise().iReturnAPromiseUnrelatedToMyParent();
return foo;
}
foo as it is, can only carry one resolution. You may do like
foo = await iReturnAPromise().then(v => (doSomeThingWith(v), iReturnAPromiseUnrelatedToMyParent()));
in which case foo will be assigned the resolution of iReturnAPromiseUnrelatedToMyParent. However if you would like to access both resolutions (which are independent of each other) then you may do like;
async function test(){
[foo,bar] = await Promise.all([Promise.resolve(10), Promise.resolve(20)]);
return [foo,bar];
}
test().then(([a,b]) => console.log(a,b));

Async / await assignment to object keys: is it concurrent?

I know that doing this:
const resultA = await a()
const resultB = await b()
// code here
Is effectively
a().then( resultA => {
b().then( resultB => {
// code here
})
})
Basically, a() runs then b() runs. I nested them to show that both resultA and resultB are in our scope; yet both function didn't run at once.
But what about this:
const obj = {
result1: await a(),
result2: await b()
}
do a() and b() run concurrently?
For reference:
const asyncFunc = async (func) => await func.call()
const results = [funcA,funcB].map( asyncFunc )
I know here funcA and funcB do run concurrently.
Bonus:
How would you represent the object assignment
const obj = {
result1: await a(),
result2: await b()
}
using then / callbacks?
UPDATE:
#Bergi is correct in this answer, this occurs sequentially. To share a nice solution for having this work concurrently for an object without having to piece together the object from an array, one can also use Bluebird as follows
const obj2 = Bluebird.props(obj)
http://bluebirdjs.com/docs/api/promise.props.html
No, every await will stop the execution until the promise has fulfilled, even mid-expression. It doesn't matter whether they happen to be part of the same statement or not.
If you want to run them in parallel, and wait only once for their result, you have to use await Promise.all(…). In your case you'd write
const [result1, result2] = await Promise.all([a(), b()]);
const obj = {result1, result2};
How would you represent the object assignment using then / callbacks?
With temporary variables for each awaited value. Every await translates into one then call:
a().then(tmp1 => {
return b().then(tmp2 => {
const obj = {
result1: tmp1,
result2: tmp2
};
return …
});
})
If we wanted to be pedantic, we'd have to pick apart the object creation:
const tmp0 = {};
a().then(tmp1 => {
tmp0.result1 = tmp1;
return b().then(tmp2 => {
tmp0.result2 = tmp2;
const obj = tmp0;
return …
});
})
do a() and b() run concurrently?
No, they run sequentially.
The equivalent would be something like
a()
.then(result1 => b())
.then(result2 => ({result1, result2}))

How to use async await function object in Javascript?

Say I have a function object-
setObj : function(a,b){
obj.a = a;
obj.b = b;
}
If I have to use async & await on this function object, how do I do it?
If the same was written in function (function way), say-
async function setObj(a,b){
obj.a = a;
obj.b = b;
}
await setObj(2,3);
This works fine. But, how do I do it in case of function object?
If I understand your question correctly, you can just use the async keyword in front of the method declaration:
let obj = {};
let myObj = {
async setObj(a,b) {
obj.a = a;
obj.b = b;
}
}
See http://tc39.github.io/ecmascript-asyncawait/#async-methods
UPDATE
You cannot use await outside of an async function. In order to use this you have to wrap that call to await setObj(2, 3):
async function consoleLog() {
await myObj.setObj(2, 3);
console.log(obj.a + obj.b);
}
consoleLog();
Use the same async keyword in your object's property:
(async function () {
var obj = {};
console.log("hello");
let setObj = async function (a,b){
obj.a = a;
obj.b = b;
};
await setObj(2,3);
console.log(obj.a+obj.b);
})();
Note that the entire code is wrapped in an asynchronous self-invoking function. This is needed, otherwise the await setObj will not be able to run correctly.
using arrow functions work as well
const myObject = {
myFunc: async () => {
await myResultHere
}
}
using this: Since the function is an async function, it will run asynchronously. If you want to run this with await, you will have to use it inside an async function
const useFunc = async () => {
const res = await myObject.myfunc();
}
You can simply put the async keyword on any function, not only function declarations but also function expressions and methods of object. For example:
As an method of an object:
const Object = {
async asyncFunction() {
await MyExamplepromise
}
}
As a variable:
const myFunc = async function () { await MyExamplepromise }
// this is how execute the function expression
// first () operator to execute, and use .then get access the resolved value
myFunc().then((val) => { console.log(val) })
Also notice that an async function 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.

Categories