When babel processes async / await code does it bundle unrelated calls? - javascript

Is babels async / await code smart enough to see the code below:
async function alpha () {
let resultOne = await processNumber(5)
let resultTwo = await processNumber(5 + 8)
let resultThree = await processNumber(resultOne.number)
let resultFour = await processNumber(resultOne.number + resultThree.number)
return resultFour
}
As something like the following, where the first two promises within this function can happen together because the values needed to perform those operations does not need to wait for anything.
import Promise from 'bluebird'
async function beta () {
let {resultOne, resultTwo} = await Promise.props({
resultOne: processNumber(5),
resultTwo: processNumber(5 + 8)
})
let resultThree = await processNumber(resultOne.number)
let resultFour = await processNumber(resultOne.number + resultThree.number)
return resultFour
}
I would understand the alpha function as waiting for each async function call before it moves on to the next, where in beta resultOne and resultTwo are happening simultaneously, this is only possible because they don't need to wait on any other calls. I was wondering if this was truly the case or if babel does something behind the scenes to bundle these together.

I setup a benchmark between the two and it seems it does not account for this on it's own.
Here's the test:
import Promise from 'bluebird'
async function processNumber (int) {
await Promise.delay(500)
return {number: int + 3}
}
async function alpha () {
let resultOne = await processNumber(5)
let resultTwo = await processNumber(5 + 8)
let resultThree = await processNumber(resultOne.number)
let resultFour = await processNumber(resultOne.number + resultThree.number)
return resultFour
}
async function beta () {
let {resultOne, resultTwo} = await Promise.props({
resultOne: processNumber(5),
resultTwo: processNumber(5 + 8)
})
let resultThree = await processNumber(resultOne.number)
let resultFour = await processNumber(resultOne.number + resultThree.number)
return resultFour
}
async function main () {
const TEST_ALPHA = 'test alpha'
const TEST_BETA = 'test beta'
console.time(TEST_ALPHA)
let resultAlpha = await alpha()
console.log(resultAlpha)
console.timeEnd(TEST_ALPHA)
console.time(TEST_BETA)
let resultBeta = await beta()
console.log(resultBeta)
console.timeEnd(TEST_BETA)
return true
}
main()
.then(console.log)
.catch(console.error)
Here's the results:
thomasreggi#zx:PAS-api$ babel-node test.js
{ number: 22 }
test alpha: 2025ms
{ number: 22 }
test beta: 1508ms
true
thomasreggi#zx:PAS-api$ babel-node test.js
{ number: 22 }
test alpha: 2033ms
{ number: 22 }
test beta: 1511ms
true

In JS it is nearly impossible to make any strict statements whether a given expression is "unrelated" to another arbitrary expression (especially statically).
It happens because due to its highly-dynamic nature almost every expression may cause hidden (or not so hidden) side effects that may break the expected flow of the program.
For your very code it is really easy to break the code in case if both "unrelated" calls are triggered "simultaneously":
let isFirst = true;
async function processNumber(v) {
await Promise.delay(2000 - v * 100);
if (v < 10) {
if (!isFirst) {
throw new Error();
}
}
isFirst = false;
return { number: v + 3 };
}
This would work for alpha, but throw for beta.
If you know it would be fine and want to run them "in parallel" just use awaits correspondingly:
async function alpha () {
let one = processNumber(5)
let two = processNumber(5 + 8)
const resultOne = await one;
let resultThree = await processNumber(resultOne.number)
let resultFour = await processNumber(resultOne.number + resultThree.number)
return resultFour
}
Also note, that the resultTwo is not used anywhere in your code.

Related

Make JS function output while function running

Javascript code
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT = 1000000;
var money = 0
var i = 0
let dollars = document.getElementById("total")
let dollarclick = document.getElementById("click1")
let autoclick = document.getElementById("buyauto")
dollarclick.addEventListener("click", clicked)
autoclick.addEventListener("click", auto)
function clicked() {
money = money + 1
dollars.innerHTML = "$"+ money.toString()
}
function auto() {
autoclick.disabled = true
console.log("function skull")
while (i<10) {
money = money + 1
dollars.innerHTML = "$"+ money.toString()
sleep(1000)
console.log(money)
i++
}
}
How to make the function output while it is running, and not let it output after the function is already finished? Keep in mind I am making a cookie clicker type game, with the auto being able to be purchased only once. Can you help me resolve this problem? The game should be able to give the points every second, and not after the function has finished. Please help me, thank you!
You can do this in an asynchronous manner.
I have used a function (setTimeoutAsync from #3 here) that resolves a promise after a timeout, and an asynchronous recursive function (nClicks(n)) that uses await for the timeout before performing the action and awaits recursion n times before returning.
Then, the asynchronous event handler just needs to disable the button and wait for nClicks(10) to complete before doing other stuff like re-enabling the button. If you don't need to do other stuff afterwards you can omit the async declaration on the handler and just call nClicks(10) without await.
const setTimeoutAsync = function(timeout) {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, timeout)
})
}
let money = 0
const dollars = document.getElementById('total')
const tick = () => {
dollars.innerHTML = `$${++money}`
console.log('tick', dollars.innerHTML)
}
const nClicks = async(n) => {
if (n) {
// wait one second
await setTimeoutAsync(1000)
// then incriment
tick()
// wait for recursion to complete
await nClicks(--n)
}
}
const dollarclick = document.getElementById('click1')
const autoclick = document.getElementById('buyauto')
dollarclick.addEventListener('click', tick)
autoclick.addEventListener('click', async() => {
autoclick.disabled = true
// wait for 10 cycles
await nClicks(10)
autoclick.disabled = false
})
const autoclickOnce = document.getElementById('buyautoOnce')
autoclickOnce.addEventListener('click', () => {
autoclickOnce.disabled = true
nClicks(10)
})
<h1 id="total">$0</h1>
<button id="click1">+1</button>
<button id="buyauto">Auto</button>
<button id="buyautoOnce">Auto Once</button>

why is my async code inside for loop not running sequentially?

Why do all my function calls get executed in parallel here when using await? What needs to be changed in order to start a function call once the previous is done?
for (let collection of collections) {
await this.updateListings(collection)
}
private async updateListings(collection: Collection) {
try {
const startTime = Date.now();
const supply = collection.stats.total_supply;
const contract_address = collection.primary_asset_contracts[0].address;
const token_ids = [];
for (let i = 1; i <= supply; i++) {
token_ids.push(i);
}
let offset = 0;
let limit = 30;
while (offset + limit <= supply) {
this.apiKey = this.apiKeys.find(key => key !== this.apiKey); //rotate api key on each request
const ids = token_ids.splice(0, 30);
const params = new URLSearchParams();
params.set("offset", offset.toString());
params.set("limit", limit.toString());
params.set("side", "1");
params.set("sale_kind", "0");
params.set("order_by", "eth_price");
params.set("order_direction", "asc");
params.set("asset_contract_address", contract_address);
for (let i = 0; i < ids.length; i++) {
params.append("token_ids", ids[i])
}
await this.delayRequest(700);
const response = await axios.get(process.env.OPENSEA_API_BASE_URL + "/orders", {
headers: {
"X-API-KEY": this.apiKey,
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'
},
params: params
})
const { orders } = response.data;
if (orders.length > 0) await getRepository(Listing).save(orders);
offset += limit;
console.log(`update listings for ${collection.slug} status: ${offset} / ${supply}`)
}
const endTime = Date.now() - startTime;
console.log("fetched listings for " + collection.slug + " in " + endTime + " ms")
} catch (error) {
console.log("updating listings for " + collection.slug + " failed", error.message);
}
}
constructor() {
setTimeout(() => {
this.update();
}, 6000)
}
What I expect is that the first iteration finishes before it proceeds but its calling each iteration "updateListings(coll)" at the same time.
In fact. your code is executed sequentially. How did you check that your code was running in parallel?
You can use Array.reduce to make it sequentially:
return collections.reduce((currentPromiseChain, collection) => {
return currentPromiseChain.then(() => this.updateListings(collection));
}, Promise.resolve());
Just add an await before the condition. This will ensure each loop finishes before continuing to the next one.
Note: not fully sure on the compatibility for this, you may need check for compatibility.
for await (let collection of collections) { //notice the await in this line.
await this.updateListings(collection)
}
This is to demostrate there isn't anything wrong with the way you are calling for loop, and also how you have a while loop inside your updateListing function.
There's probably something you didn't show?
The only reason I can see, affecting your codes, is if there are errors, and your try/catch block returns the promise with error immediately, making it appears it is running in parallel.
const whileLoopFunction = async(value) => {
let limit = 0;
let supply = 10
while (limit < supply) {
await new Promise(resolve => setTimeout(resolve,100))
limit++
}
console.log('While loop complete for value ', value)
};
(async() => {
const collections = [ 1, 2, 3, 4, 5 ]
console.log("For loop works with await")
for (let collection of collections) {
await whileLoopFunction(collection)
}
})()

JavaScript: async function that would retry for a period of time at a given interval

I am trying to write an async function that is going to await a function that got passed as one parameter and I wanted this async function to retry this operation for 5 mins every 10 seconds.
I found one function that sort of does this but it retries based on the numbers of times instead.
async function retry(fn, n) {
for (let i = 0; i < n; i++) {
try {
const ret = await fn();
if(!ret) throw new Error() // if `ret` is null or undefined, we will retry.
return ret
} catch {}
}
throw new Error(`Failed retrying ${n} times`);
}
Is there a way to tweak this function to satisfy my use cases?
Since your function is async, you can easily create timeouts to wait between subsequent calls:
const sleep = t => new Promise(r => setTimeout(r, t))
async function retry(fn) {
const startTime = Date.now()
while(Date.now() - startTime < 5 * 60 * 1000) {
try {
const ret = await fn();
if(ret)
return ret
// if `ret` is null or undefined, we won't return.
} catch {}
await sleep(10 * 1000)
}
throw new Error(`Failed retrying`);
}

Value Undefined: Loops in async await with Promise.race?

I am working on a project where I need to brute force PDF password.
For that PDF.js is used for verifying password and promise.race to run parallel functions to make the overall work fast.
and this is how i implemented it:
var sfile = "KRIS.pdf"
var dBuf = fs.readFileSync(sfile);
const tryCases = getCombos(alphaArray, 4); // permutation having total length 456976
var finishFunction = false;
async function getPass(startAt = Number(), endAt = Number()) {
var isDone = false;
var currentPass = ''
for (let index = startAt; index < endAt; index++) {
if (finishFunctions) { return; }
currentPass = tryCases[index].join("");
await pdfjsLib.getDocument({ data: dBuf, password: currentPass }).promise
.then((r) => {
console.log('Worked: ' + currentPass);
isDone = true;
pdfjsLib.distroy();
return new Promise.resolve();
})
.catch((e) => { })
if (isDone) {
finishFunctions = true;
return currentPass;
}
}
console.log(`Hey Nothing From ${startAt} - ${endAt}`);
}
console.time('Found ');
Promise.race([
getPass(0, 100000),
getPass(100000, 200000),
getPass(200000, 300000),
getPass(300000, 400000),
getPass(400000, 456976)
])
.then((s) => { console.timeEnd('Found '); console.log('HeyThen ' + s) })
.catch((e) => console.log('Hey Error ' + e));
now it works to get the 4 letter password but There are problems preventing it from being complete.
First Current Function is very very slow, it takes forever even after running parallel functions.
Second I added a flag to stop other parallel functions but it does not work as expected with 4 Letter Forcing.
Third Resource usage is really high. My Linux system stops responding.
for visualizing the time and flag issue I used 3 letters forcing and here is log of it:
Worked: KRIS
Found: 13950.743ms
HeyThen KRIS
Hey Nothing From 4934 - 8788
Hey Nothing From 0 - 4394
Hey Nothing From 13182 - 17576
(node:3068) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
By calling return new Promise.resolve(currentPass); you return a result to then and not from ATryCase. Write the result of await myFunc.getDocument to some variable and return it
const res = await myFunc.getDocument({ data: dBuf, pos: currentPass })
.promise
.then(() => {
console.log('Data is: ' + currentPass);
console.timeEnd('GotData ');
return new Promise.resolve(currentPass);
}).catch(e => { });
return res;
if the currentPass is correct, i need to break the loop and return the value
None of your code snippets did that so far. You will want to use something like
async function ATryCase(indexStart, indexEnd) {
for (let index = indexStart; index < indexEnd; index++) {
var currentPass = startLetters + index;
try {
await myFunc.getDocument({ data: dBuf, pos: currentPass }).promise;
console.log('Data is: ' + currentPass);
return currentPass; // this breaks the loop and `return`s from ATryCase
} catch {
continue;
}
}
throw new Error("No pass did find a result");
}
for that.
Alternatively, have a look at the proposed Promise.any, which would do exactly what you are looking for, but concurrently. (You will also want to use it in place of Promise.race if you go for 4 concurrent sequential searches). You could use it like
function ATryCase(indexStart, indexEnd) {
const promises = [];
for (let index = indexStart; index < indexEnd; index++) {
var currentPass = startLetters + index;
promises.push(myFunc.getDocument({ data: dBuf, pos: currentPass }).promise.then(() => {
console.log('Data is: ' + currentPass);
return currentPass;
}));
}
return Promise.any(promises);
}

nodejs - Chaining dependent asynchrous operations

I have two asynchronous operations such that the invocation of the second one uses input from the output of the first. To implement such invocations with async and await, it seems, it won't turn out to be too different from callbacks.
Consider this.
async function loop(label, I) {
console.log('%s - Looping for %d times.', label, I)
console.time(label)
for (i = 0; i < I; ++i) {
}
return Promise.resolve(I)
}
//
async function demo0() {
// Refer - https://stackoverflow.com/a/45479579/919480
let I0 = loop('01', 10)
let I1 = loop('02', I0 * 1000)
await I0
await I1
}
//
async function demo1() {
// Refer - https://stackoverflow.com/a/45479579/919480
let I0 = loop('11', 10)
await I0
let I1 = loop('12', I0 * 1000)
await I1
}
//
async function demo2() {
await loop('21', 10).then(async (i) => {
await loop('22', i * 1000)
})
}
//
(async () => {
await demo0()
await demo1()
await demo2()
})()
Result:
01 - Looping for 10 times.
02 - Looping for NaN times.
11 - Looping for 10 times.
12 - Looping for NaN times.
21 - Looping for 10 times.
22 - Looping for 10000 times.
The second loop should iterate based on a value passed on by the first loop. In demo0 and demo1, the second loop receives a NaN because they are triggered simultaneously. Only in demo2, does the second loop receive the correct value. One could have achieved this behavior with callbacks too.
Is there a async/await way to achieve this behavior?
Every call of an async function gives you a Promise in return, but Promises can't be (sensibly) added to numbers, so you get NaN in return.
If you want to be able to use the result of a Promise, you should await it and use the resulting expression. If you await the Promise and then try to use the original Promise, you'll still have a Promise, not a value, so you should assign the awaited Promise to a variable, eg change
let I0 = loop('01', 10)
let I1 = loop('02', I0 * 1000)
await I0
await I1
to
let I0 = loop('01', 10)
const resolveValueI0 = await I0;
let I1 = loop('02', resolveValueI0 * 1000)
await I1
(you cannot call the second loop until I0 is done, because the second loop needs the number from the resolution of I0's Promise. Either that, or pass the Promise to I1, and have I1 properly consume it with .then or await)
and
let I0 = loop('11', 10)
await I0
let I1 = loop('12', I0 * 1000)
await I1
to
let I0 = loop('11', 10)
const resolveValueI0 = await I0;
let I1 = loop('12', resolveValueI0 * 1000)
await I1
async function loop(label, I) {
console.log('%s - Looping for %d times.', label, I)
console.time(label)
for (i = 0; i < I; ++i) {
}
return Promise.resolve(I)
}
//
async function demo0() {
// Refer - https://stackoverflow.com/a/45479579/919480
let I0 = await loop('01', 10)
let I1 = loop('02', I0 * 1000)
await I0
await I1
}
//
async function demo1() {
// Refer - https://stackoverflow.com/a/45479579/919480
let I0 = loop('11', 10)
const result = await I0
let I1 = loop('12', result * 1000)
await I1
}
//
async function demo2() {
await loop('21', 10).then(async (i) => {
await loop('22', i * 1000)
})
}
//
(async () => {
await demo0()
await demo1()
await demo2()
})()

Categories

Resources