Wait for async function in multiple places - javascript

With Promises I can have two separate "threads" both waiting for the same value:
let trigger;
const promise = new Promise(r => {
console.log('promise is created *once*');
trigger = value => {
console.log('trigger is called *once*');
r(value);
}
});
(async () => {
console.log('A waiting');
const value = await promise;
console.log(`A finished, got ${value}`);
})();
(async () => {
console.log('B waiting');
const value = await promise;
console.log(`B finished, got ${value}`);
})();
trigger('hello');
console.log('And *two* things are waiting on the single promise');
I've tried to replicate this with async/await but to no avail.
The following snippet does not work:
let trigger = async () => {
console.log('trigger should be called *once*');
return 'hello';
};
(async () => {
console.log('A waiting');
const value = await trigger; // <-- What do I need to put here?
console.log(`A finished, got ${value}`);
})();
(async () => {
console.log('B waiting');
const value = await trigger; // <-- What do I need to put here?
console.log(`B finished, got ${value}`);
})();
trigger(); // <-- How can this "kick off" the two awaits above?
Is it possible to write the same functionality in the first snippet, using async/await?
I'm okay with falling back to using Promise if that's needed.

To await, you need to have a reference to the singular promise, so you can't call a function on demand and have that function create a promise and then use that same promise elsewhere (unless the function that creates the promise also holds it in state to return to other callers, like a singleton).
I'd create a singular promise initially, and then send it to the async functions:
const trigger = async () => {
console.log('trigger should be called *once*');
return 'hello';
};
async function as1(prom) {
console.log('A waiting');
const value = await prom;
console.log(`A finished, got ${value}`);
}
async function as2(prom) {
console.log('B waiting');
const value = await prom;
console.log(`B finished, got ${value}`);
}
const thePromise = trigger();
as1(thePromise);
as2(thePromise);
Don't use async just to return a promise, though - if the purpose of a function is to create a promise, then do it explicitly - that way, it's more clear what your code is meant to do. Async and await have not made the Promise keyword obsolete, it's just syntax sugar which is useful in some situations (and unnecessary in others).

Related

Function returns a pending promise, but value of variable is returned into it

This is the code
function readNotes(){
return new Promise((resolve, reject) => {
user = firebase.auth().currentUser.uid
ref = database.ref("users/" + user + "/notes")
ref.get().then( function(snapshot) {
if (snapshot.exists()) {
keys= Object.keys(snapshot.val())
console.log(keys)
resolve(keys);
} else {
console.log("No data available");
}
}).catch((error) => {
reject(error);
})
})
}type here
I tried calling the function in the console and I was expecting for it to return a fulfilled promise, especially because I was able to log the value of the keys variable
Your function returns Promise. (hence return new Promise(...) at the beginning of your function);
You will need to either await (if you are inside an async function) like so:
const notes = await readNotes();
console.log(notes);
or you can use .then() like so:
readNotes().then(notes => {
console.log(notes)
}).catch(err => {
console.error(error);
}
Note: do not forget .catch to intercept errors.
If you are not in async function, but want to use async/await you can use an IIFE (Immediately Invoked Function Expression).
(async () => {
const notes = await readNotes();
console.log(notes);
})()

How to execute asynchronous tasks in order

I'm currently experimenting with asynchronous functions in Javascript and I stumbled upon a case where I wanted to execute a collection of asynchronous functions in the order that they are placed in an array.
I also want the possibility to pass an argument to an asynchronous function or not. With my current solution, the two functions with a passed argument are executed first and at the same time, then the other two functions are executed one after the other.
const asyncOne = async (value = "no value passed to async 1") => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(value);
resolve();
}, 1000);
});
};
const asyncTwo = async (value = "no value passed to async 2") => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(value);
resolve();
}, 1000);
});
};
const sequence = async tasks => {
const resolve = Promise.resolve(null);
await tasks.reduce(
(promise, task) =>
promise.then(() => (typeof task === "function" ? task() : task)),
resolve
);
};
(async () => {
const res = await sequence([asyncOne("first"), asyncTwo, asyncOne, asyncTwo("last")]);
})();
Expected output:
"first"
"no value passed to async 2"
"no value passed to async 1"
"last"
Actual output:
"first"
"last"
"no value passed to async 2"
"no value passed to async 1"
As I mentioned in my comment, you should not mix new Promise with async – there's no need to, and you'll just confuse yourself and any future readers.
Either way, an async function will begin to execute synchronously, which is why you see your sequence (both the first and last timeouts begin at the same moment). To fix this, you'd need to use a function that calls the function that returns a promise, like so:
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function asyncOne(value = "async1 default") {
await delay(1000);
console.log(value);
}
async function asyncTwo(value = "async2 default") {
await delay(1000);
console.log(value);
}
async function sequence(tasks, init = undefined) {
let value = init;
for (let task of tasks) {
if (typeof task === "function") value = await task(value);
else value = await task;
}
return value;
}
(async () => {
await sequence([
() => asyncOne("first"),
asyncTwo,
asyncOne,
asyncTwo.bind(this, "last"), // can also use `bind` instead of an anonymous arrow function
]);
})();
This implementation also lets you chain values from one promise to another, and allows for an initial value to be passed to the first promise-returning function, á la
await sequence([asyncTwo], "hello!");
asyncOne("first") and asyncTwo("last") call the functions and execute them immediately, so it prevents you from executing them in order.
Instead, write asyncOne.bind(this, "first"), which does not execute the function immediately and allows you to execute it later.
(async () => {
const functions = [
asyncOne.bind(this,"first"),
asyncTwo,
asyncOne,
asyncTwo.bind(this, "last")
];
for(let f of functions){
await f()
}
})();

Await / Async with a conditional statement and IIAEF

Description
I want to conditionally call a db SELECT within a non-async function. I wrapped it in a IIAEF? like so
async function getScopedDataPolicies() {
const test = new Promise((res) => {
console.log('2')
res('test1')
})
return test;
}
function nonAsyncMethod() {
(async () => {
console.log('1')
const test = getScopedDataPolicies();
const policies = await test;
console.log('3')
})()
console.log('4')
}
nonAsyncMethod()
My expectation is
1
2
3
4
But I get
1
2
4
3
What wouldn't the await work in the immediately invoking method??
Look at these examples; am I missing something?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function
All await does is pause the execution of the current function, until an async task has finished, at which point execution will continue. It does not pause all of Javascript, that kind of behavior is synchronous programming, and Javascript is built to avoid it, because it makes for a really bad user experience if Javascript has to stall every time you make a rest request, etc.
update
Some possible workarounds:
await the IIFE, or take out the IIFE entirely. The IIFE is an async function, so it'll return a promise when it'scalled. You're not awaiting that promise, which is why your outer sync function continues on without waiting for it to finish.
async function nonAsyncMethod() {
await (async () => {
console.log('1')
const test = getScopedDataPolicies();
const policies = await test;
console.log('3')
})()
console.log('4')
}
Move console.log('4') into your IIFE. I know that console.log('4') might represent a whole log of code, but in some cases, you've just got to move it all inside - break up your function into smaller functions if you have to.
await inside the IIFE doesn't effect the outer function
To do so, you could either do this:
async function getScopedDataPolicies() {
const test = new Promise((res) => {
console.log('2')
res('test1')
})
return test;
}
async function nonAsyncMethod() {
await (async () => {
console.log('1')
const test = getScopedDataPolicies();
const policies = await test;
console.log('3')
})()
console.log('4')
}
nonAsyncMethod()
or this:
async function getScopedDataPolicies() {
const test = new Promise((res) => {
console.log('2')
res('test1')
})
return test;
}
function nonAsyncMethod() {
(async () => {
console.log('1')
const test = getScopedDataPolicies();
const policies = await test;
console.log('3')
})().then(() => {
console.log('4')
})
}
nonAsyncMethod()
Note: if you need to "wait" for nonAsyncMethod - you can only do await nonAsyncMethod(), or nonAsyncMethod.then(() => { continue here })

async await, process one item of For loop at a time

I can't seem to wrap my head around async await, let's say I want to process one item of an array at a time, with a 1 second delay:
myfunction() {
clearTimeout(this.timer);
this.timer = setTimeout(() => {
for (const item of this.myarray) {
this.dosomething(item).then((res: string) => {
setTimeout(() => {
await this.final_thing_before_the_next_item.push(res);
}, 1000);
});
}
}, 200);
}
Where would I put the "async"?
Stackblitz demo
You can put an async inside the setTimeout:
myfunction() {
setTimeout(async () => {
for (const item of this.myarray) {
await this.wait(1000);
console.log(item);
}
});
}
wait(timer: number): Promise<void> {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), timer);
});
}
In a simplified form, await is syntax sugar for consuming promises. The following snippets are pretty much equivalent:
doSomethingAsync().then(value => {
console.log('it finished', value);
});
const value = await doSomethingAsync();
The caveat is that if you want to use await, you need to mark the function as async. I will assume you want to have a 1s delay after full completion. Your code might look something like this:
function wait() {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function processItems(items) {
for (const item of items) {
const res = await this.doSomething(item);
// This isn't processed until doSomething completes.
const other = await this.doFinalThing(res);
// This isn't run until doFinalThing completes.
await wait(1000);
// The loop now continues after waiting 1s.
}
}
you marked this as angular, so I'm going to say, consider rxjs...
// imports
import {timer, from, concat} from 'rxjs'
import {switchMap, delay} from 'rxjs/operators'
// wait 200 ms
timer(200).pipe(
// switch
switchMap(() => {
// map array into streams
const obs$ = this.myArray.map(i => {
// from promise, do whatever
return from(this.dosomething(i)).pipe(
// switch again, to do final thing, again from promise
switchMap(res => from(this.finalThing(res))),
// delay 1s
delay(1000)
)
});
return concat(...obs$); // execute one after the other
})
).subscribe(v => console.log('values 1 by 1, spaced 1s apart', v))
If you want to process one asynchronous operation at a time, you could do the following:
// "wait" for a 1000ms before resolving the Promise
const wait = (ms = 1000) => {
return new Promise(resolve => {
setTimeout(() => resolve(), ms);
});
};
// array of asynchronous operations
const promises = Array.from({ length: 4 }).map((_, idx) =>
Promise.resolve(idx)
);
async function main() {
const data = [];
for (let item of promises) {
const result = await item;
await wait(1000);
data.push(result);
console.log("result", result);
}
console.log("final data", data);
}
main();
If I understand your question (and code sample) correctly, you basically want to
Sleep 200 ms
Iterate over a list of items. For each item you want to:
Call a function, passing the current item to it and getting a response in return.
Pause for 1 second.
Call a second function, passing it the response
To do that you need a sleep() function that returns a promise that will resolve when the specified time has elapsed:
function sleep(ms = 1000) {
const p = new Promise( (resolve, reject) => {
setTimeout(() => resolve());
});
return p;
}
Having that, you need an async function to do the actual work. It has to be async because that is what lets it await other things:
async function process_items( items, delayInMs = 1000 ) {
for ( item of items ) {
const res = await doSomething( item, delayInMs );
await sleep(delay);
await doSomethingElse( res );
}
}
As you can see from the above bit of code, the advantage of using async/await over callbacks or promise chains is that it gives you a rather more succinct and declarative syntax.
Then you can wrap it all up in another async function:
async function myfunction() {
await sleep(200);
await process_items( this.myarray, 1000 );
}
Marking a function as async does two things: it
Enables the use of await within that function, and
Converts the function into a function returning a promise, regardless of what its ostensible return value is.
If you take this function:
function foo() {
return 1;
}
and mark it as async:
async function foo() {
return 1;
}
it is (more or less) as if you had changed to to read:
function foo() {
return Promise.resolve(1);
}
The easiest way, without looking at the specifics of your code is before myFunction ie async myFunction(). I see 'this' being used so I imagine this is a method of some object/class in which case that would be one way to go about it.

Calling async function from within async function returns undefined

(Forgive me if the title is inaccurate to the problem this one is boggling the mind)
My application requires that I check a value from the request against the database. For this I created an asynchronous function to query the database:
async function checktoken(){
return prisma.$exists.invalidtoken({token: "testtoken"}).then(result => {
return(result)
})
}
I know that the database call on its own works:
prisma.$exists.invalidtoken({token: "testtoken"}).then(result => {
console.log(result) // returns true
})
In the function that fires at every request I try and call checktoken():
async function getUser(token){
var promise = await checktoken()
var result = promise
console.log(result) //undefined
};
Amending the function to include an explicit call to the database works but only when var promise = await checktoken() is defined before it:
async function getUser(token){
var promise = await checktoken() //When this is removed result1 is undefinded
await prisma.$exists.invalidtoken({token: "testtoken"}).then(result1 => {
console.log("inside db call: "+result1) // true
})
};
I think I have a fundamental misunderstanding of async/await but I am not sure exactly what I am missing.
EDIT:
I have updated my approach taking the advice I received and it still does not work. I am beginning to think my ORM is doing something weird:
async function test(token) {
const status = await prisma.$exists.invalidtoken({ token: token });
console.log(status);
return status;
}
test("123") //logs false (as it should)
async function getUser(token){
var status = await test(token) //logs undefined
console.log(status) //logs undefined
};
An async function requires an await. If you are using the promise.then() technique, then what you would want to do is return a new Promise(), and within the .then call back function resolve the promise
function checktoken() {
return new Promise((resolve,reject) => {
prisma.$exists.invalidtoken({token: "testtoken"}).then(result => {
doSomeOtherStuff();
resolve(result);
});
});
}
Is functionally the same as
async checktoken() {
await prisma.$exists.invalidtoken({token: "testtoken"});
}

Categories

Resources