The following behavior of Javascript is a complete mystery to me. The following code snippet does not work:
let someFunctionOne = async () => (new Promise(resolve => setTimeout(resolve(1), 1000)))
let someFunctionTwo = async () => (new Promise(resolve => setTimeout(resolve(2), 1000)))
let someFunctionThree = async () => (new Promise(resolve => setTimeout(resolve(3), 1000)))
let myFunction = async () => {
let resultOne
if (true) {
let resultTwo, resultThree // <-- doesn't work
if (false) {
throw new Error('Something went wrong')
}
const promiseOne = someFunctionOne()
const promiseTwo = someFunctionTwo()
const promiseThree = someFunctionThree()
[resultOne, resultTwo, resultThree] = await Promise.all([
promiseOne,
promiseTwo,
promiseThree]
)
resultOne = {resultOne, resultTwo, resultThree}
}
return resultOne
}
let result = await myFunction()
console.log(result)
This throws the following error:
ReferenceError: Cannot access 'promiseThree' before initialization
However, when the initialization of the two variables is put right above the Promise.all() call it does work:
let someFunctionOne = async () => (new Promise(resolve => setTimeout(resolve(1), 1000)))
let someFunctionTwo = async () => (new Promise(resolve => setTimeout(resolve(2), 1000)))
let someFunctionThree = async () => (new Promise(resolve => setTimeout(resolve(3), 1000)))
let myFunction = async () => {
let resultOne
if (true) {
if (false) {
throw new Error('Something went wrong')
}
const promiseOne = someFunctionOne()
const promiseTwo = someFunctionTwo()
const promiseThree = someFunctionThree()
let resultTwo, resultThree // <-- does work
[resultOne, resultTwo, resultThree] = await Promise.all([
promiseOne,
promiseTwo,
promiseThree]
)
resultOne = {resultOne, resultTwo, resultThree}
}
return resultOne
}
let result = await myFunction()
console.log(result)
Why on earth is the second code snippet working and the first one isn't? For both, the variable initialization of resultTwo and resultThree happen before they are used...
Thanks
Related
const fun = (x) => {
console.log(`Meth... ${x}`);
setTimeout(() => {
onReceive(`Fun Received: ${x}`);
}, 100);
return x;
}
const publish = () => {
return new Promise(resolve => {
window.onReceive = (token) => {
resolve(token);
}
});
}
const Inst = () => {
const getAs = async(x) => {
console.log(`Getting As ----- ${x}`);
const res = await publish(fun(x));
console.log(`Res is '${res}' ---- ${x}`);
}
return {
getAs,
}
}
const inst = Object.freeze(Inst());
const a = async() => {
await inst.getAs(1);
await inst.getAs(2);
await inst.getAs(3);
await inst.getAs(4);
await inst.getAs(5);
};
a();
const b = async() => {
await inst.getAs(4);
await inst.getAs(5);
await inst.getAs(6);
await inst.getAs(7);
};
b();
// Comment or remove `b()` to see the different result in Console
I got confused why the function only called the last async await (b() from the example). Why inside of a() seems like didn't get called at all??
If I comment / remove b(), all inside of a() will get call.
Need helps / explanation to resolve such issue 🙏.
I have a few promises that I have to iterate with the generator. I created the helper function that prints the first promise but I have difficulties creating an iterator in a helper function to iterate all promises. I actually don't understand how constant a, constant b, and constant c can be iterated.
const asyncTask1 = () => new Promise((resolve, reject) => setTimeout(() => resolve('first resolved'), 1000));
const asyncTask2 = () => new Promise((resolve, reject) => setTimeout(() => resolve('second resolved'), 1000));
const asyncTask3 = () => new Promise((resolve, reject) => setTimeout(() => reject('third rejected'), 1000));
async function helper(func) {
let p = func().next().value
let output = await p
console.log(output)
}
helper(function* main() {
try {
const a = yield asyncTask1()
console.log(a);
const b = yield asyncTask2()
console.log(b);
const c = yield asyncTask3()
} catch (e) {
console.error('error happened', e);
}
})
Output: first resolved
Should be:
first resolved
second resolved
third rejected
You are calling the generator only once using .next() only once so the function would yield only once. Also, when you can func() you are creating a new generator every time. Your try catch block is in generator inside which is yield, so it won't catch the error since yield would not throw an error. Finally, third task will not be a success, it will throw an error as it is rejected.
I will do it as below -
const asyncTask1 = () => new Promise((resolve, reject) => setTimeout(() => resolve('first resolved'), 1000));
const asyncTask2 = () => new Promise((resolve, reject) => setTimeout(() => resolve('second resolved'), 1000));
const asyncTask3 = () => new Promise((resolve, reject) => setTimeout(() => reject('third rejected'), 1000));
async function execute() {
try {
const iterator = generator();
let p = iterator.next().value;
let output = await p;
console.log(output);
let q = iterator.next().value;
let output1 = await q;
console.log(output1);
let r = iterator.next().value;
let output2 = await r;
console.log(output3);
} catch (e) {
console.log('error happened', e);
}
}
const generator = function* generator() {
yield asyncTask1();
yield asyncTask2();
yield asyncTask3();
}
execute();
Referring to #Felix Kling's comment, a more elegant way to do this would be -
const asyncTask1 = () => new Promise((resolve, reject) => setTimeout(() => resolve('first resolved'), 1000));
const asyncTask2 = () => new Promise((resolve, reject) => setTimeout(() => resolve('second resolved'), 1000));
const asyncTask3 = () => new Promise((resolve, reject) => setTimeout(() => reject('third rejected'), 1000));
async function execute() {
const iterator = generator();
for await (const task of iterator) {
try {
let output = await task();
console.log(output);
} catch (e) {
console.log('error happened', e);
}
}
}
const generator = function* generator() {
yield*[asyncTask1, asyncTask2, asyncTask3];
}
execute();
I have an array of asynchronous functions, it is necessary to call in order and the result of the call of the previous function is passed into the arguments. How can this be done approximately?
// lets say we have a function that takes a value and adds 20 to it asynchronously
const asyncPlus20 = num => Promise.resolve(num+a)
const arr = [asyncPlus20, asyncPlus20]
let res = 0 // some starting value
for (const f of arr) res = await f(res)
// res is now 20
One of the best way for Array of Async functions is to use For...of.
Run the below snippet in the console. >> Also includes the argument passing
const twoSecondsPromise = () => {
return new Promise((resolve) => {
setTimeout(() => resolve('2000_'), 2000);
})
};
const threeSecondsPromise = (val) => {
return new Promise((resolve) => {
setTimeout(() => resolve(val + '3000_'), 3000);
})
};
const fiveSecondsPromise = (val) => {
return new Promise((resolve) => {
setTimeout(() => resolve(val + '5000_'), 5000);
})
};
(async function () {
const asyncFunctions = [twoSecondsPromise, threeSecondsPromise, fiveSecondsPromise];
let result;
for (const file of asyncFunctions) {
result = await file(result);
console.log(result);
}
})();
I have a function:
fn = () => {
fn1(); // call first
fn2(); // call only when listener is done from fn1
}
and these two fns:
fn1 = () => {
const k1 = new Image();
k1.src = someSrc;
k1.addEventListener('load', () => some func {});
}
My question: What to do to make fn2 function fire only when fn1 is called and load event is fired?
const fn = async () => {
await fn1(); // call first
await fn2(); // call only when listener is done from fn1
}
const fn1 = async () => {
const k1 = new Image();
k1.src = await 'https://www.medicalnewstoday.com/content/images/articles/326/326253/corgi-and-terrier-running.jpg';
await k1.addEventListener('load', async () => await console.log('it should log first'));
}
const fn2 = async () => {
console.log('then second');
}
fn();
If you want to work with async/await, as mentioned in the comments, then you'll need to create and return a promise:
const fn1 = () => {
return new Promise((resolve) => {
const k1 = new Image();
k1.src = someSrc;
k1.addEventListener('load', resolve);
});
}
Then you can await this promise in an async function:
const fn = async () => {
await fn1();
fn2();
}
Try something like this:
const fn1 = () => {
const k1 = new Image();
k1.src = 'https://www.medicalnewstoday.com/content/images/articles/326/326253/corgi-and-terrier-running.jpg';
document.body.appendChild(k1);
return new Promise(resolve => {
k1.addEventListener('load', () => resolve('I am done!'));
})
}
const fn2 = () => {
console.log('then second');
}
const fn = async () => {
const a = await fn1(); // call first
console.log(a);
fn2(); // call only when listener is done from fn1
}
fn();
Why I can't access object properties with a . in async await return object?
I know that I can access the properties like below.
let val1 = await call(3);
let val2 = await call(4);
but I'm interested if this can be done
let v = await call(3).val + await call(4).val;
const call = (x) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
val: x
});
}, 3000)
})
}
const dummy = async () => {
//let val1 = await call(3);
//let val2 = await call(4);
//alert(value.val + val2.val);
let v = await call(3).val + await call(4).val;
alert(v);
}
dummy()
You're trying to await the value of the val property of the promise.
You need to await the promise and then read the val property from the result: (await call(3)).val
const call = (x) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
val: x
});
}, 3000)
})
}
const dummy = async () => {
let v = (await call(3)).val + (await call(4)).val;
alert(v);
}
dummy()
Just wrap the await and the expression to await in parens. Then access the property.
const call = (x) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
val: x
});
}, 3000)
})
}
const dummy = async() => {
let v = (await call(3)).val + (await call(4)).val;
alert(v);
}
dummy()
Do note that doing it this way, you're awaiting 3 seconds for the first call, then awaiting another 3 seconds for the second call. Unless the second call is somehow reliant on the first, I suggest you do the following:
const call = (x) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
val: x
});
}, 3000)
})
}
const dummy = async() => {
// Call both in parallel
let val1 = call(3);
let val2 = call(4);
// Await for both
let v = await Promise.all([val1, val2]);
// *then* add
alert(v[0].val + v[1].val);
}
dummy()
Why I can't access object properties with a . in async await return
object?
Because call returns a Promise and thus has no property val. When you await the returned Promise, the expression await call(x) will eventually resolve to {val: x}, which you can then use .val on.
Thus, you can either await each call separately and save the returned object in their own variables, or wrap your expression await call(x) in parenthesis of their own, such that you are getting the .val of the resolved value, not the promise:
const call = (x) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
val: x
});
}, 3000)
})
}
const dummy = async() => {
let v = (await call(3)).val + (await call(4)).val;
alert(v);
}
dummy()
This is because the call does not return a direct result object. It is returning a promise which will resolve value in .then callback. You can await this call.
const call = (x) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
val: x
});
}, 3000)
})
}
const dummy = async () => {
//let val1 = await call(3);
//let val2 = await call(4);
//alert(value.val + val2.val);
let v = await call(3).then(result => result.val) + await call(4).then((result)=> result.val);
alert(v);
}
dummy()