while await... in JavaScript - javascript

The following code uses an async iterator to deliver "frames".
But this approach leaves an unused variable (_).
If this is a valid approach, why was while await (a hypothetical feature) not added when for await...of was?
while await would enable the omission of this ignored variable.
const raf = () => new Promise(resolve => requestAnimationFrame(resolve))
const frames = {
async *[Symbol.asyncIterator]() {
while (true) yield raf()
}
}
function createGameLoop(store) {
async function start() {
for await (let _ of frames)
render(store)
}
return { start }
}

why was while await (a hypothetical feature) not added when for await...of was?
Because it adds unnecessary complexity to the language implementation, and is rarely ever useful.
Generators ought to produce values, not nothings. Ignoring the value is easy by not using the variable, as you did. The main use case is getting values, and that's what the syntax was designed for.
They actually would be useful even in your example. Your generator doesn't deliver nothing, it doesn't deliver "frames", it does deliver the high resolution timestamps that requestAnimationFrame calls resolve with, and you should be using them in your render function for smooth delta animation:
function createGameLoop(store) {
async function start() {
for await (const timestamp of frames)
render(store, timestamp)
}
return { start }
}

Related

Optional chaining and await

Is there any reason to use if to check if method exists, before calling await
if (someObject.save){
await someObject.save();
}
rather than using nullish coallescence diectly with await
await someObject.save?.();
Is second code a safe alternative to the former?
Is second code a safe alternative to the former?
Yes. But maybe not for the reason one might expect.
The two pieces of code are equivalent in terms of the check made:
const obj = {
foo() { console.log("method called"); },
bar: 42
};
if (obj.foo) {
console.log("obj has foo");
obj.foo(); //works
}
console.log("using optional chaining");
obj.foo?.(); //works
if (obj.bar) {
console.log("obj has bar");
try {
obj.bar(); //error
} catch (err) {
console.log("bar cannot be called");
}
}
console.log("using optional chaining");
try {
obj.bar?.() //error
} catch (err) {
console.log("bar cannot be called");
}
.as-console-wrapper {
max-height: 100% !important
}
However, in terms of async semantics, there is a difference: await will force an async function to pause. While not having await will run the function until another await is encountered or the function finishes and a result is produced.This can lead to very subtle bugs. Consider this case when the behaviour overlaps - there is a save() method, so both functions pause to await its result:
let x = 1;
async function testIf(someObject) {
console.log("testIf 1:", x); //some external variable
if (someObject.save){
await someObject.save();
}
console.log("testIf 2:", x); //some external variable
}
async function testOptional(someObject) {
console.log("testOptional 1:", x); //some external variable
await someObject.save?.();
console.log("testOptional 2:", x); //some external variable
}
const obj = {
save() { return Promise.resolve(); }
}
testIf(obj);
testOptional(obj);
x = 2; //change external variable
The behaviour is consistent: both of these will stop at the line with await, which will yield the execution, which then processes the new assignment to x, so then when both functions resume, they both read the new value of x. This is expected.
Now, consider what happens if the await line is not hit in the if version:
let x = 1;
async function testIf(someObject) {
console.log("testIf 1:", x); //some external variable
if (someObject.save){
await someObject.save();
}
console.log("testIf 2:", x); //some external variable
}
async function testOptional(someObject) {
console.log("testOptional 1:", x); //some external variable
await someObject.save?.();
console.log("testOptional 2:", x); //some external variable
}
const obj = {}
testIf(obj);
testOptional(obj);
x = 2;
The semantics changed. For the if version the await line is not processed, thus the execution does not yield until the function finishes, thus it reads the value of x before the reassignment both times. The version that uses optional chaining preserves its semantics even if save is not present - it still encounters the await and still yields.
In summary, having optionally asynchronous operation is the path to madness. I would suggest avoiding it at all costs.
Read more about this:
Callbacks, synchronous and asynchronous by Havoc
Designing APIs for Asynchrony by Isaac Z. Schlueter
Intentionally unleashing Zalgo with synchronous promises by Daniel Brain
And yes, the code I showed is also probably bad - you most likely should not rely on external values. Yet, sometimes you have to. And even if you do not rely on them, in the future, somebody consuming your code might. It is best to just avoid Zalgo in the first place and have more predictable code.
The best answer to this (I think) would be if you are working in a group where the group has decided not to use ?. because not everyone understands it. Otherwise, no. The second isn't safer
It should be fine as you would await undefined.
Some considerations from the documentation:
const result = someInterface.customMethod?.();
if there is a property with such a name which is not a function, using ?. will still raise a TypeError exception "someInterface.customMethod is not a function".
Note: If someInterface itself is null or undefined, a TypeError exception will still be raised ("someInterface is null"). If you expect that someInterface itself may be null or undefined, you have to use ?. at this position as well: someInterface?.customMethod?.().

What happens if you make a function async and change nothing else?

I have a sudoku board implemented as an HTML table and a button that when clicked solves the sudoku board using a recursive backtracking algorithm in javascript. Now I want to make it so that you can see the adjustments being made by not altering the HTML immediately in my recursive function. I tried making the function async and then calling this function
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
like so
for (let choice = 1; choice <= 9; choice++) {
this.boardArray[row][col] = choice;
// put delay here
await sleep(250);
currEntry.textContent = choice;
if (this.choiceOkay(row, col)) {
const solved = this.solveBoard(nextRow, nextCol);
if (solved) {
return true;
}
}
This does give me the desired behavior initially but only for a portion of the board and then it seems to just stop. I removed the calls to the sleep function and tried it with the only alteration being "async" in front of the function declaration and it still only did a portion of the board but this time all at once without the visual delay. I'm wondering why making this function async causes this logic error?
Thanks in advance!
Also, this is my first question on Stack Overflow so let me know if I need to be more specific or anything along those lines.
The async keyword in front of a function basically says:
This function will return a Promise
Your sleep function already returns a Promise, so writing the keyword async is useless.
What matters is the await keyword which basically says:
On my right, there may be an async function (idem a Promise). Wait for my return before continuing
If you write async function sleep, but omit the await keyword when you call sleep, you throw the function call in the nature and never waits for its return to come back (hence your code running "without delay")
If you want delay, it is better to let your algorithm be as is, and let the caller of your algorithm tell your algorithm to continue or not.
You can for that matter use generators.
(An other possibility could be trampolining).
function* runAlgo () {
for (let choice = 1; choice <= 9; choice++) {
yield; // gives control back to delayer
console.log('choice : ', choice)
// do your algo and put some yield wherever you like
}
}
// no need for async, a Promise is already returned
function sleep (t) {
return new Promise((ok, ko) => setTimeout(ok, t))
}
async function delayer () { // need for async so we can use the await keyword below
const it = runAlgo()
let next = it.next()
while (!next.done) {
await sleep(1000)
next = it.next()
}
}
delayer()
However, it is very likely that solving your board is what freezes your ui.
So you want to wait inside of solvingBoard as well:
function* solveBoard () {
let i = 0
while (i < 5) { //freezes the ui if not yielding
yield i++
}
}
function* runAlgo () {
for (let choice = 1; choice <= 9; choice++) {
yield 'choice : '+choice; // gives control back to caller
yield* solveBoard()
}
}
// no need for async, a Promise is already returned
function sleep (t) {
return new Promise((ok, ko) => setTimeout(ok, t))
}
async function delayer () { // need for async so we can use the await keyword below
const it = runAlgo()
let next = it.next()
while (!next.done) {
await sleep(1000)
next = it.next()
console.log('data', next.value)
}
}
delayer()

Why to use while when it is always true?

I see most of the examples in redux-saga using while(true){}:
function* watcherSaga(){
while (true) {
yield something()
}
}
Can we not simply write?
function* watcherSaga(){
yield something()
}
Or, is there anything difference?
Take a look at the following example. One generator is never "done" and the other generator is done after the first (and only) yield.
function something() {
return Math.random();
}
function* watcherSaga1() {
while (true) {
yield something();
}
}
function* watcherSaga2() {
yield something();
}
const watcher1 = watcherSaga1();
const watcher2 = watcherSaga2();
console.log('watcher1: ', watcher1.next());
console.log('watcher1: ', watcher1.next());
console.log('watcher1: ', watcher1.next());
console.log('watcher2: ', watcher2.next());
console.log('watcher2: ', watcher2.next());
console.log('watcher2: ', watcher2.next());
With the while loop, the generator will continue to yield values forever. Without, it's just once.
When you have a need for some sequence (say, multiples of 7), a function like that can easily supply such a sequence to a consumer that imposes its own limits on how many values it needs.
Generators provide an extremely powerful way of structuring code, and have a way of permeating a design in some cases. They're particularly useful in the context of media generation, like p5.js, where there's lots of interacting iteration. Generators provide a particularly nice way to encapsulate that.
Generators returns an Iterator.
When you call next() on the iterator, it produces the return value and if the function comes to end, it will generate an undefined value and gets done.
Check the below snippet. Hope this one helps you.
function* watcherSaga() {
var i = 0;
while (true) {
yield i++;
}
}
const sagaIterator = watcherSaga();
console.log("**** with while() loop *****");
console.log(sagaIterator.next());
console.log(sagaIterator.next());
console.log(sagaIterator.next());
// You can keep calling sagaIterator.next() and it never gets "done" because of "while(true)"
function* watcherSagaWithoutWhile() {
var i = 0;
yield i++;
}
const sagaIteratorWİthoutWhite = watcherSagaWithoutWhile();
console.log("**** withOUT while() loop *****");
console.log(sagaIteratorWİthoutWhite.next());
console.log(sagaIteratorWİthoutWhite.next());
console.log(sagaIteratorWİthoutWhite.next());
// The second call to "next()" will return an "undefined" value and the generator gets done because the generator comes to the end
For further reading: https://basarat.gitbooks.io/typescript/docs/generators.html

How to wait for an async function in javascript at top level?

I know this is a terrible idea. But I have an API which I can't use until I have a piece of data which I can only get asynchronously. Something like this:
const key = await get_async_data(config) // NOT RIGHT, can't use await at top level
const api = new API(key)
... use api ...
This is at top level, outside of any function, so I can't just await get_async_data() (it does return a Promise).
Is there anything short of putting all my code in a giant async function so I can call await?
API is just a class exported by a module (which I control).
(BTW I thought of putting the code to get the key into the API class's constructor, but of course constructors can't be async either.)
I could make every async method of API set the key if unset, but that's pretty invasive and error-prone.
So I'm not really asking how to make my top-level code wait as much as I'm looking for alternative ways to structure this so the async call happens cleanly.
Here's some more detail in case this helps.
In api.js:
class API {
constructor(key) {
this.key = key
// other stuff
}
async f1(a) {
}
async f2(b, c) {
}
f3() {
return true
}
}
export default API
Then in the places (many) where it'll be used:
import API from '#/api'
const key = async get_key() // NOPE
const theAPI = new API(key)
async importantMethod(args)
{
return await theAPI.f1(args)
}
async otherMethod()
{
if (theAPI.f3)
return await theAPI.f2(123)
// etc...
}
// ... and so on
Just use the Promise:
const pendingAPI = get_async_data(config).then(key => new API(key)); // N.B. no await
export default pendingAPI;
Meanwhile, in another file...
import pendingAPI from 'other/file';
pendingAPI.then(doStuffWithAPI);
There are times when async/await is a win. But never forget it's just sugar over Promises.
top level is a terrible idea, yes. But I don't see why you can't just put it in a function?
const getAsync = async () => {
const key = await get_async_data(config);
return key;
}
getAsync().then(key => {
const api = new API(key)
}
If you want to alter your existing code as little as possible, I'd consider changing the entry point to a module which gets the key, and then calls the module which instantiates the API (the old entry point). For example:
// start.js
import makeApi from './makeApi';
get_key()
.then(makeApi);
// makeApi.js
export default function(key) {
const theApi = new API(key);
function importantMethod(args) {
return theAPI.f1(args)
}
function otherMethod() {
if (theAPI.f3)
return theAPI.f2(123)
}
// etc
}
In short, all you have to do is wrap your current entry point in a function.

Converting await in infinite for loop into raw promise.then

As far as I know, async/await is just syntactic sugar over promise.then. Consider this code snippet:
function sleep(n){
return new Promise(res => setTimeout(res, n));
}
function* range(n){
var i = 0;
while(i < n) yield i++;
}
async function doStuff(){
for(let n of range(10)){
console.log(n); // print the number
await sleep(1000); // wait for 1 second
}
}
async/await makes the code very linear, efficient and easy to understand. One thing to keep in mind is that range does not have to have an actual end for this to work.
The problem now is how this can be rewritten using pre-ES7 era's promise.then. Here's a possible implementation of the same loop:
function doStuff(){
return Array.from(range(10)).reduce((acc, ele) => {
return acc
.then(() => console.log(ele))
.then(() => sleep(1000))
}, Promise.resolve());
}
Ignoring the fact that the code isn't quite elegant, the use of Array.from(range(10))
creates an extra array that isn't needed, and
assumes range(10) will end some point in the future.
Doesn't look like a good conversion.
We can also completely reinvent the wheel by using yield as await, but that would make the syntax non ES5-compliant. The goal here is to:
Rewrite using ES5-compliant syntax
Use the promise-returning sleep function
Dynamically chain the sleep promise while allowing the iterator to not have an end
doStuff can be chained:
doStuff().finally(cleanUp); // clean up if something failed
(Optional) Code should not be overly complex
Any idea?
I think the following may do the trick, your example doesn't show what to do with resolve value and how it relates to the iterator values so I made a change to how sleep is called.
Some promise polyfils may run out of stack space with high promise chains so you should check your polyfil (if its implementation returns and continues with a setTimeout the stack should clear but some polyfils may not implement it this way).
function sleep(n){
return new Promise(res => setTimeout(_=>res(n/100), n));
}
function* range(n){
var i = 0;
while(i < n) yield i++;
}
function doStuff(){
const processValue =
resolve => {
console.log("resolved with:",resolve);
// if(resolve===3){throw "nope";}
return sleep(resolve*100);
},
rec = (p,iter) => {
const result = iter.next();
if (result.done){
return p;
}
p = p.then(_=>processValue(result.value))
return p.then(
resolve=>{
return rec(p,iter)
}
);
},
iter = range(10),
firstResult = iter.next();
if(firstResult.done){
return processValue(firstResult.value);
}
return rec(processValue(firstResult.value),iter);
}
doStuff()
.then(
x=>console.log("done:",x)
,reject=>console.warn("fail:",reject)
);
I've always said that if you need an asynchronous design pattern first look at the async library. In this case, since you're using promises, take a look at the promisified async-q library. The translation is straight forward:
var n = 0;
async.whilst(() => n < 10, () => {
n++;
console.log(n);
return sleep(1000);
})

Categories