how to call async function in response - javascript

I need to use await in a response function, but I couldn't figure out how. (Note: func2() is an async function)
async function get_data() { 
for (i = 0; i < 10; i++) {
// http request, returns json...
await func1(data1, data2).then(json => {   
if (json.value < 100) {
await func2(); //error => await is only valid in async function
}  
}) 
}
}
I have to wait for func2, how can I do that?
I tried this:
async function get_data() { 
for (i = 0; i < 10; i++) {
await func1(data1, data2).then(json =>
async function() { // nothing happens, function doesnt work.
if (json.value < 100) {
await func2();   
}  
}) 
}
}

It's generally easier if you don't mix await and .then(). Since you want to sequence two operations and it appears you want the for loop to be sequenced too, it's simpler with just await.
async function get_data() {
 for (let i = 0; i < 10; i++) {
  let json = await func1(data1, data2);
  if (json.value < 100) {
    await func2();
  }
 }
}

You can get rid of .then() and use await instead.
await func1 and get the value and then check if the value satisfies the condition and if it does await func2.
Check the demo snippet below:
async function get_data() {
for (i = 0; i < 10; i++) {
const { value } = await func1("Hello", "World");
if (value < 100) {
console.log(await func2(value));
}
}
console.log("END!")
}
const func1 = (d1, d2) =>
new Promise((res) => setTimeout(() => res({ value: Math.random(0) * 200 }), 200));
const func2 = (v) => new Promise((res) => setTimeout(() => res(v), 200));
get_data();

Related

Javascript Promise with setTimeout

const sleep = (time) => {
return new Promise((resolve) => setTimeout(resolve, time))
}
const doSomething = async () => {
for (let i = 0; i < 100; i++) {
await sleep(1000)
console.log(i)
}
}
doSomething()
Why is this promise working as expected? I'm learning about Promise and stuck on this. The function passed into the Promise constructor does not have reject() and yet it is working.
The snippet is from this article: https://medium.com/javascript-in-plain-english/javascript-slow-down-for-loop-9d1caaeeeeed
I'm also struggling to understand the explanation in the article about the differences between the setTimeout approach (that does not work) vs. setTimeout in promise approach (that works).
Thanks for any pointer on this topic.
Arguments are optional in JavaScript so the reject argument is just ignored.
As for why the first example doesn't work is that setTimeout registers an event for the event loop to call in the future. Things in JavaScript do not block. Internally await rewrites the code so that it does not block, but the code still reads as synchronous, but it is not.
There are plenty of great examples out there explaining the JavaScript event loop, and I'd highly suggest reading up on it.
The Promise is not resolved until the resolve ("res") function is called. The function that gets passed to setTimeout does not run until the timeout finishes.
Here is an alternative example, where the resolve function returns the count that gets passed-in.
const main = async () => {
const done = await doSomething(10, 1000, i => console.log(i));
console.log('Done!');
};
const doSomething = async (times, duration, callback) => {
for (let counter = 0; counter < times; counter++) {
const count = await sleep(counter, duration);
callback(count);
}
return true;
};
const sleep = (count, duration) => {
return new Promise((res) => setTimeout((n) => res(n), duration, count));
};
main();
.as-console-wrapper { top: 0; max-height: 100% !important; }
Here is a less-coupled version, with some default parameters.
const DEBUG = true;
const main = async () => {
const worker = (n, m) => console.log(`Step ${n + 1}/${m}`);
await doSomething(worker);
};
const doSomething = async (callback, duration = 1000, times = 10) => {
if (DEBUG) console.log('Start!');
for (let counter = 0; counter < times; counter++) {
await sleep(duration, counter, times);
callback(counter, times);
}
if (DEBUG) console.log('Done!');
return true;
};
const sleep = (duration, count = 1, times = 1) => {
return new Promise(res => setTimeout(res, duration));
};
main();
.as-console-wrapper { top: 0; max-height: 100% !important; }

Javascript cancel async for loop

I found this code in a project:
const fn = async () => {
let x = 0;
for(let i = 0; i < 50; i++){
const res = await api.call(i);
if(res.someProp) x++;
}
return x;
}
I want to be able to stop it mid way, so that if I call it again, it will start from scratch and discard the previous call results. To avoid making two sets of requests at the same time.
This should do:
let token;
const fn = async () => {
const my = token = Symbol();
let x = 0;
for(let i = 0; i < 50 && my == token; i++){
const res = await api.call(i);
if(res.someProp) x++;
}
return x;
}
While there still can be some overlap between the calls, any previous loops will break their iteration as soon as the next fn() call is started.
You can use any technique of using an external flag variable to break the loop.
As a workaround you can try to use a custom Promise class (Live demo):
import CPromise from "c-promise2";
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
async function api(i) {
console.log(`Async API call [${i}]`);
await delay(100);
return {};
}
const fn = () =>
CPromise.from(function* () {
let x = 0;
for (let i = 0; i < 50; i++) {
const res = yield api.call(i);
if (res.someProp) x++;
}
return x;
});
const cancelablePromise = fn().then(
() => console.log("Done"),
(err) => console.log(`Fail: ${err}`) // Fail: CanceledError: canceled
);
setTimeout(() => {
cancelablePromise.cancel(); // abort the async sequence (loop) after 3500ms
}, 3500);

Async/Await is not working as expected with for loop statement

I am new to Javascript and nodejs. While trying to understand how promises and callbacks work, i tried calling a function in a 'for' loop as shown below. What i was trying to achieve is to print 'i' value every 2 seconds using promises. However, the program waits for 2 seconds and prints i value 3 times and exits.
for(let i = 0; i < 3 ; i++){
func(callback,i);
}
async function func(callback,i){
await callback(i);
}
function callback(i){
return new Promise((res,rej) =>{
setTimeout(()=>{
console.log('i = ',i)
res();
}, 2000);
})
}
Can anybody please help me understand why this is happening?
You are pretty close. The missing information is async functions (your func()) implicitly returns an AsyncFunction object which implicitly returns a Promise itself too (doc).
Your code using immediately invoked function expression would be
(async () => {
for(let i = 0; i < 3 ; i++){
await func(callback,i);
}
})()
async function func(callback,i){
await callback(i);
}
function callback(i){
return new Promise((res,rej) =>{
setTimeout(()=>{
console.log('i = ',i)
res();
}, 2000);
})
}
Please note now in most of the browsers is natively available the construct for await..of (doc). You can try experimenting this one too.
You can just wrap your loop with async immediately executed function and add await within it (as was already suggested here):
(async () => {
for(let i = 0; i < 3 ; i++){
await callback(i);
}
})();
function callback(i){
return new Promise((res,rej) =>{
setTimeout(()=>{
console.log('i = ',i)
res();
}, 2000);
})
}
Here is my original answer before Bergi's edit:
(async () => {
for(let i = 0; i < 3 ; i++){
await func(callback,i);
}
})()
async function func(callback,i){
await callback(i);
}
function callback(i){
return new Promise((res,rej) =>{
setTimeout(()=>{
console.log('i = ',i)
res();
}, 2000);
})
}
You need to wait for async function to complete
for(let i = 0; i < 3 ; i++){
await func(callback,i);
}
But since you can't use await keyword in the global scope, you will need to wrap your for loop in an async function, and than call it
async function myTest(){
for(let i = 0; i < 3 ; i++){
await func(callback,i);
}
}
myTest()
Here are some functions which help you understand how promises work with Arrays, where we make common mistakes.
function promiseFunction(v) {
return new Promise((resolve) => {
setTimeout(() => resolve(v), 1000);
});
}
function f1() {
[1, 2, 3, 4]. forEach(async(i) => {
const v = await promiseFunction(i);
console.log(`-f1 v- ${v}<br/>`);
});
console.log('all done<br/>');
}
async function f2() {
await Promise.all([1, 2, 3, 4].map(async(i) => {
const v = await promiseFunction(i);
console.log(`-f2 v- ${v}<br/>`);
}));
console.log('all done<br/>');
}
async function f3() {
await [1, 2, 3, 4].reduce((p, i) => {
return p.then(async () => {
const v = await promiseFunction(i);
console.log(`-f3 v- ${v}<br/>`);
});
}, Promise.resolve());
console.log('all done<br/>');
}
async function func() {
console.log('f1');
await f1();
console.log('f2');
await f2();
console.log('f3');
await f3();
}
func();
f1() will print all done first and then print 1,2,3,4 at once after a second.
f2() will print 1,2,3,4 at once after a second then print all done.
f3() will print 1,2,3,4 in every second, and then print all done.
You async function func also returns a promise so you need the await keyword before its call.
for(let i = 0; i < 3 ; i++){
await func(callback,i);
}

Using javascript's Symbol.asyncIterator with for await of loop

I am trying to understand javascript's Symbol.asyncIterator and for await of. I wrote some simple code and it throws an error saying:
TypeError: undefined is not a function
on the line which tries to use for await (let x of a).
I could not understand the reason for it.
let a = {}
function test() {
for(let i=0; i < 10; i++) {
if(i > 5) {
return Promise.resolve(`Greater than 5: (${i})`)
}else {
return Promise.resolve(`Less than 5: (${i})`)
}
}
}
a[Symbol.asyncIterator] = test;
async function main() {
for await (let x of a) { // LINE THAT THROWS AN ERROR
console.log(x)
}
}
main()
.then(r => console.log(r))
.catch(err => console.log(err))
I create an empty object a and insert a key Symbol.asyncIterator on the same object and assign it a function named test that returns a Promise. Then I use for await of loop to iterate over all the values that the function would return.
What am I doing incorrectly?
PS: I am on the Node version 10.13.0 and on the latest version of Chrome
To be a valid asyncIterator, your test function must return an object with a next method that returns a promise of a result object with value and done properties. (Technically, value is optional if its value would be undefined and done is optional if its value would be false, but...)
You can do that in a few ways:
Completely manually (awkward, particularly if you want the right prototype)
Half-manually (slightly less awkward, but still awkward to get the right prototype)
Using an async generator function (simplest)
You can do it completely manually (this doesn't try to get the right prototype):
function test() {
let i = -1;
return {
next() {
++i;
if (i >= 10) {
return Promise.resolve({
value: undefined,
done: true
});
}
return Promise.resolve({
value: i > 5 ? `Greater than 5: (${i})` : `Less than 5: (${i})`,
done: false
});
}
};
}
let a = {
[Symbol.asyncIterator]: test
};
async function main() {
for await (let x of a) {
console.log(x)
}
}
main()
.then(r => console.log(r))
.catch(err => console.log(err))
You can do it half-manually writing a function that returns an object with an async next method (still doesn't try to get the right prototype):
function test() {
let i = -1;
return {
async next() {
++i;
if (i >= 10) {
return {
value: undefined,
done: true
};
}
return {
value: i > 5 ? `Greater than 5: (${i})` : `Less than 5: (${i})`,
done: false
};
}
};
}
let a = {
[Symbol.asyncIterator]: test
};
async function main() {
for await (let x of a) {
console.log(x)
}
}
main()
.then(r => console.log(r))
.catch(err => console.log(err))
Or you can just use an async generator function (easiest, and automatically gets the right prototype):
async function* test() {
for (let i = 0; i < 10; ++i) {
yield i > 5 ? `Greater than 5: (${i})` : `Less than 5: (${i})`;
}
}
let a = {
[Symbol.asyncIterator]: test
};
async function main() {
for await (let x of a) {
console.log(x)
}
}
main()
.then(r => console.log(r))
.catch(err => console.log(err))
About prototypes: All async iterators you get from the JavaScript runtime itself inherit from a prototype that provides the very basic feature of ensuring the iterator is also iterable (by having Symbol.iterator be a function returning this). There's no publicly-available identifer or property for that prototype, you have to jump through hoops to get it:
const asyncIteratorPrototype =
Object.getPrototypeOf(
Object.getPrototypeOf(
async function*(){}.prototype
)
);
Then you'd use that as the prototype of the object with the next method that you're returning:
return Object.assign(Object.create(asyncIteratorPrototype), {
next() {
// ...
}
});
The test function must not return a promise, but an Iterator (an object with a next() ) method, that method then has to return a Promise (which makes it an async iterator) and that Promise has to resolve to an object containing a value and a done key:
function test() {
return {
next() {
return Promise.resolve({ value: "test", done: false });
}
};
}
Now while that works, it is not that useful yet. You could however create the same behaviour with an async generator function:
async function* test() {
await Promise.resolve();
yield "test";
}
Or in your case:
async function* test() {
for(let i = 0; i < 10; i++) {
if(i > 5) {
await Promise.resolve();
yield `Greater than 5: (${i})`;
}else {
await Promise.resolve();
yield `Less than 5: (${i})`;
}
}
}
You should make test an async generator function instead, and yield instead of return:
let a = {}
async function* test() {
for(let i=0; i < 10; i++) {
if(i > 5) {
yield Promise.resolve(`Greater than 5: (${i})`)
}else {
yield Promise.resolve(`Less than 5: (${i})`)
}
}
}
a[Symbol.asyncIterator] = test;
async function main() {
for await (let x of a) {
console.log(x)
}
}
main()
.then(r => console.log(r))
.catch(err => console.log(err))
It looks like the test function needs to be async so that the x in the for await gets unwrapped, even though test doesn't await anywhere, otherwise the x will be a Promise that resolves to the value, not the value itself.
yielding Promise.resolve inside an async generator is odd, though - unless you want the result to be a Promise (which would require an extra await inside the for await loop), it'll make more sense to await inside the async generator, and then yield the result.
const delay = ms => new Promise(res => setTimeout(res, ms));
let a = {}
async function* test() {
for(let i=0; i < 10; i++) {
await delay(500);
if(i > 5) {
yield `Greater than 5: (${i})`;
}else {
yield `Less than 5: (${i})`;
}
}
}
a[Symbol.asyncIterator] = test;
async function main() {
for await (let x of a) {
console.log(x)
}
}
main()
.then(r => console.log(r))
.catch(err => console.log(err))
If you didn't make test a generator, test would have to return an iterator (an object with a value property and a next function).

Output results of two promises into a single object

I have two asynchronous functions which create promises. Both functions use the same input i.
async function1(i) {}
async function2(i) {}
I will be calling these functions multiple times with different values for my input, and I want to make my code as efficient as possible, so I want to queue the promises and have them run in parallel using Promise.all().
However, I want to get a single result as my final output, which will be an array of objects like so:
[
{
input: i,
result1: result1,
result2: result2
},
...
]
I have accomplished this in two descrete steps:
async function function1(i) {
return i * i
}
async function function2(i) {
return i * i * i
}
async function main() {
var promises = []
for (let i = 0; i < 10; i++) {
let promise = function1(i)
.then(function(result1) {
return {i:i, result1:result1}
});
promises.push(promise)
}
var final = await Promise.all(promises)
var promises2 = [];
for (let i = 0; i < 10; i++) {
let promise = function2(i)
.then (function(result2) {
final[i]['result2'] = result2;
});
promises2.push(promise);
}
await Promise.all(promises2)
console.log(final)
}
main()
However, I feel like this can be accomplished using a single Promise.all(). Can you tell me how?
async function function1(i)
{
return i * i
}
async function function2(i)
{
return i * i * i
}
async function main()
{
const promises = [];
for (let i = 0; i < 10; i++)
promises.push(function1(i), function2(i));
const results = await Promise.all(promises);
const data = [];
for (let i = 0; i < 10; i++)
{
const [result1, result2] = results.slice(i * 2, i * 2 + 2);
data.push({ i, result1, result2 });
}
console.log(data);
}
main();
This should work smoothly and quickly. The functions function1 and function2 return Promises if you do not await them, so pushing them onto the promises array is self-explanatory.
Then you await Promise.all, which waits until all 20 promises that were fired off have completed. Finally, the second loop runs through the promises that were returned.
In my code I used destructuring assignments. I assume those are available to you since you are using async functions, which implies that you can use ES2017.
EDIT: 333's answer is correct, not mine. This solution will have 2 batches in the queue.
I think I figured it out:
console.log("start")
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function function1(i) {
await sleep(2000);
return i * i
}
async function function2(i) {
await sleep(2000);
return i * i * i
}
async function main() {
var promises = []
for (let i = 0; i < 10; i++) {
let a = function1(i);
let b = function2(i);
let promise = Promise.all([a,b]).then(
function([resulta,resultb]) {
return {i:i, result1: resulta, result2: resultb}
});
promises.push(promise)
}
var final = await Promise.all(promises)
console.log(final)
}
main()
Note that I added a sleep function to test the parallelization of these functions. Everything runs in 2 seconds, so I think it is optimized.

Categories

Resources