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}))
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);
}
See the two code blocks below:
for await (const {key, value} of ctx.stub.getStateByRange(startKey, endKey)) {}
and
let allCarsRangeIterator = await ctx.stub.getStateByRange(startKey, endKey);
for (const {key, value} of allCarsRangeIterator ) {}
When I use the first one, everything works, but when I use the second one, I get an error saying allCarsRangeIterator is not iterable.
Why are these two not equivalent ?
I found this to be a very interesting question and dug around for a bit. Let's see if I can answer this properly:
First, we need to be clear about iterators and generators, so I encourage everyone to go through the linked documentation.
The following seems to be a generator:
ctx.stub.getStateByRange(startKey, endKey)
As per the docs, a generator is a special type of iterator that can be iterated over only once. On top of that, it seems to be a generator that does some async computation. Let's mock this with a simpler function:
async function* foo() {
yield 1;
yield new Promise((resolve) => setTimeout(resolve, 2000, 2));
yield 3;
}
As per the docs, in order for an object to be iterable, it needs to implement the Symbol.asyncIterator (async) or Symbol.iterator (sync) protocol. Let's check this:
async function* foo() {
yield 1;
yield new Promise((resolve) => setTimeout(resolve, 2000, 2));
yield 3;
}
(async function () {
const obj1 = foo();
console.log(typeof obj1[Symbol.iterator] === "function"); // false
console.log(typeof obj1[Symbol.asyncIterator] === "function"); // true
})();
Now let's see the output if we use await.
async function* foo() {
yield 1;
yield new Promise((resolve) => setTimeout(resolve, 2000, 2));
yield 3;
}
(async function () {
const obj1 = await foo();
console.log(typeof obj1[Symbol.iterator] === "function"); // false
console.log(typeof obj1[Symbol.asyncIterator] === "function"); // true
})();
It's still the same. This conforms to the error you got: allCarsRangeIterator is not iterable, because obj1 does not implement the Symbol.iterator protocol but rather the Symbol.asyncIterator protocol.
So, when you are trying to use for (const num of await foo()), it is a synchronous code, so it is going to look for Symbol.iterator protocol implementation. But since your function is an async generator, it only has Symbol.asyncIterator protocol implemented.
The only way to iterate over an async generator is therefore:
for await (const num of foo()) {
console.log(num);
}
You will also see that this is valid (although it looks weird)
for await (const num of await foo()) {
console.log(num);
}
Since (const num of await foo()) still has the Symbol.asyncIterator protocol implemented.
In fact, in VSCode, you will see a warning whenever you type for (const num of await foo()):
Type 'AsyncGenerator<any, void, unknown>' must have a '[Symbol.iterator]()' method that returns an iterator. ts(2488)
and const obj1 = await foo() shows the warning:
'await' has no effect on the type of this expression. ts(80007)
Additional resources:
for...await of
iterable and iterator protocols
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));
I have a simple code
async function foo() {
const b = bar()
return 2 + b
}
async function bar() {
return 2
}
(async() => {
console.log(typeof foo())
})()
and it logs object. Not NaN. How does it happen that number + object -> object?
From what I remember from + spec if one of the operands is a primitive and second is an object then object should be converted to primitive. In this case with .valueOf() method
It think it's because async functions are not resolved yet in your case, becaue there is no await. So you are getting promise objects instead of your result.
see these cases:
async function foo () {
const b = await bar() //await result
return 2 + b
}
async function bar () {
return 2
}
;(async () => {
console.log(typeof await foo()) //number
})()
async function foo () {
const b = bar() //no await
return 2 + b
}
async function bar () {
return 2
}
;(async () => {
console.log(typeof await foo()) //string
})()
The functions foo and bar return a promise, so, you're getting the type of promise (Object)
Probably you want to compare the result, so you need to wait for promise resolves the concatenation:
let result = await foo();
^
async function foo() {
const b = bar() // Here you need to wait as well.
return 2 + b; // 2 + "[object Promise]"
}
async function bar() {
return 2
}
(async() => {
let result = await foo();
console.log(result);
console.log(typeof result);
})()
Now, to get the NaN value you need to convert to number:
async function foo() {
const b = bar(); // Here you need to wait as well.
return Number(2 + b); // <- Try to conver to number
}
async function bar() {
return 2
}
(async() => {
let result = await foo();
console.log(result);
console.log(typeof result);
})()
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.