Async await like syntax without ppx

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 :point_down:. 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.

1 Like

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)

  1. Promise may get rejected.
  2. 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]โ€