I came up with a solution for async await like syntax in Rescript. This is just my own opinion or way (may not be the ideal way for others) to solve the async await problem. I found it to be more readable than raw promise chaining. Article is available here . Comments are welcome.
This is interesting. It looks better than promise chaining, even using the pipe-first chains. Iโm glad people are thinking innovatively about this; lack of async and await are the only reason Iโm hesitant to use Rescript in production environments.
praveen how do you handle errrors in your async code?
@mouton The solution doesnโt currently handle the errors. That is left to the user just like what async/await
does. There are two possibility of errors (this applies to async/await
in js too)
- Promise may get rejected.
- First function in the sequence can throw error before promise is created.
Case 1 has to be handled with promise catch
block and Case 2 has to be handled with try catch
block. One solution that I personally prefer is to convert the Js.Promise.t<'a>
to Js.Promise.t<result<'a, 'b>
.
This can be done by creating a function that can do that for us.
let convertToResult = f => {
try {
f()
|> Js.Promise.then_(d => Ok(d)->Js.Promise.resolve)
|> Js.Promise.catch(e => Js.Promise.resolve(Error(#PromiseError(e))))
} catch {
| Js.Exn.Error(e) => Js.Promise.resolve(Error(#Exn(e)))
}
}
This function can now be used while calling asyncSequence
like below.
let f = () =>
asyncSequence3(
~a=() => failwith("This will be handled by try catch"),
~b=_ => Js.Promise.resolve(1),
~c=_ => Js.Promise.reject(Js.Exn.anyToExnInternal("This will be handled in catch block if failwith in ~a is removed")),
)
let promiseResult = f->convertToResult
May be we can improve the asyncSequence
to do that for us.
@not-rusty your example for promises with user is a little unfair dont you think? Instead of passing user down in a tuple, why not contain the rest of the chain inside the user promise resolution?
const updateJohnUsername2 = () => {
return findUser('John')
.then((user) => {
updateUsername(user, 'John Cena muy bien')
.then(({message}) => message ) // pluck message
.then(reportMessage)
.then((result) => {
if (reportingResult.status === 200) {
console.log('Reporting success')
} else {
console.log( `Failed reporting user ${user.name} updating process`)
}
})
})
.catch(() => reportMessage('User John not found'))
}
To avoid promise nesting, I always try to keep my code as flat as possible (except my spaguetti react code). I can easily compose promises if I keep them flat.
And careful with your updateUsername
call, you should return it. Luckily rescript is good enought that catches this silly mistakes.
absolutely nothing wrong with nesting here imo
โAs flat as possible [and no flatter]โ