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);
Related
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);
}
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();
This is my orginal function
function fun1(){
this.fun2 = function(){
this.fun3 = function(){
}
}
}
when I call function
new fun1().fun2()
its working fine, but when I use
new fun1().fun2().fun3()
its not working. I need to work nested function call like
new fun1().fun2().fun3()
You wrote both fun1 and fun2 in the form of a constructor (uses this, does not return a value). Constructors, if invoked using the new keyword, produce a new object.
Thus, new fun1() returns an object that has .fun2. But obj.fun2() returns undefined, since it has no return, and new was not used, which is why your .fun3 call doesn't work. You would need to write one of these:
new (new fun1().fun2)().fun3() // if `fun3` is a function
new (new (new fun1().fun2)().fun3)() // if `fun3` is a constructor
to execute fun3 in your scenario. As Rory says, this code is almost certainly a bad idea.
In order for chaining new fun1().fun2().fun3() to work, fun1 should be a constructor that creates an object that fun2 method, which returns an object that has fun3 method. An obvious scenario is when these objects are the same object, but it is not necessarily so. The easiest scenario where your chaining code would work would be
class fun1 {
fun2() {
return this;
}
fun3() {
}
}
Or equivalently
function fun1() {
}
fun1.prototype = {
fun2: function fun2() {
return this;
},
fun3: function fun3() {
},
};
Or almost-but-not-quite equivalently:
function fun1() {
this.fun2 = function fun2() {
return this;
}
this.fun3 = function fun3() {
}
}
You can also write it so that the two intermediate objects are different:
class Other {
fun3() {
}
}
class fun1 {
fun2() {
return new Other();
}
}
(or the equivalent, or almost-equivalent code)
Also note that constructors are conventionally named using TitleCase; this notifies the programmers who read the code of how it is supposed to be used. Thus, Fun1 should be strongly preferred over fun1 — though it is not an error to do otherwise.
(NB. I talk about a difference between constructors and functions; in fact, there is no difference. I merely write so as a shortcut for "a function written in such a way that it is expected to execute it as a constructor", and "a function which is written in such a way that it should be invoked as a normal function".)
I want to know the best way to retrieve the variable tag and msgCommit in the another method
public async run(commande){
...
}
public async test(){
const tag = 123
const msgCommit = await this.run('git rev-parse ' + tag )
...
}
public async test2(){
// How can I retrieve tag and msgCommit
...
}
Thanks in advance for your help :)
You're variables within test() have blocked-scope (as they're defined using const) and so cannot be accessed outside your function unless you return them. To do so, you need to wrap them in some sort of container such as an array:
public async test(){
const tag = 123
const msgCommit = await this.run('git rev-parse ' + tag )
return [tag, msgCommit];
}
And now, you can access your variables tag and msgCommit inside your test2() method by calling test() within it.
However, all async functions will return a Promise implicitly (with the original return set as the resolve), and so, when you call test() you will get a Promise. This means your array will be wrapped within this Promise. To "extract" your array from the Promise, you can await (or use .then() on) the promise to get its contents once it has resolved:
public async test2() { // `async` so we can use `await` inside this method
// How can I retrieve tag and msgCommit
const [tag, msgCommit] = await test(); // destructure your array and store the elements as variables
console.log(tag, msgCommit);
}
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));