Exploring ReScript Exception Handling

kevanstannard

Kevan Stannard

Posted on November 21, 2020

Exploring ReScript Exception Handling

Introduction

A brief exploration of some ReScript exception handling.

Hopefully this provides some useful examples of ReScript error handling.

ReScript version: bs-platform@8.3.3

Handling JavaScript exceptions thrown from JavaScript

Write a JS function that throws an error, and add a binding for it.

%%raw(`
function a() {
  throw new Error("An error occurred")
}
`)

@bs.val external a: unit => unit = "a"
Enter fullscreen mode Exit fullscreen mode

Catch the error using try/catch.

try {
  a()
} catch {
| Js.Exn.Error(error) => {
    // error: Js.Exn.t
    Js.log(Js.Exn.name(error))
    Js.log(Js.Exn.message(error))
  }
}
Enter fullscreen mode Exit fullscreen mode

Or, catch the error using switch and matching on the Js.Exn.Error type:

switch a() {
| exception Js.Exn.Error(error) => {
    // error: Js.Exn.t
    Js.log(Js.Exn.name(error))
    Js.log(Js.Exn.message(error))
  }
| () => ()
}
Enter fullscreen mode Exit fullscreen mode

Or, catch the error using switch, matching on any exception type, and detect the Js.Exn.t type.

switch a() {
| exception error => {
    // error: exn
    let optError = Js.Exn.asJsExn(error)
    switch optError {
    | Some(error) => {
        // error: Js.Exn.t
        Js.log(Js.Exn.name(error))
        Js.log(Js.Exn.message(error))
      }
    | None => Js.log("Unknown error")
    }
  }
| () => ()
}
Enter fullscreen mode Exit fullscreen mode

Handling JavaScript exceptions thrown from ReScript

Write a function that throws a JavaScript exception:

let b = (): unit => {
  // Equivalent to throw new Error("An error occurred")
  Js.Exn.raiseError("An error occurred")
}
Enter fullscreen mode Exit fullscreen mode

Handling these errors is identical to when the exception is thrown from JavaScript.

Catch the error using try/catch:

try {
  b()
} catch {
| Js.Exn.Error(error) => {
    // error: Js.Exn.t
    Js.log(Js.Exn.name(error))
    Js.log(Js.Exn.message(error))
  }
}
Enter fullscreen mode Exit fullscreen mode

Or, catch the error using switch and matching on the Js.Exn.Error type:

switch b() {
| exception Js.Exn.Error(error) => {
    Js.log(Js.Exn.name(error))
    Js.log(Js.Exn.message(error))
  }
| () => ()
}
Enter fullscreen mode Exit fullscreen mode

Or, catch the error using switch, matching on any exception type, and detect the Js.Exn.t type.

switch b() {
| exception error => {
    // error: exn
    let optError = Js.Exn.asJsExn(error)
    switch optError {
    | Some(error) => {
        // error: Js.Exn.t
        Js.log(Js.Exn.name(error))
        Js.log(Js.Exn.message(error))
      }
    | None => Js.log("Unknown error")
    }
  }
| () => ()
}
Enter fullscreen mode Exit fullscreen mode

Handling ReScript exceptions

Write a function that throws a custom ReScript exception:

exception MyException(string)

let c = (): unit => {
  raise(MyException("An error occurred"))
}
Enter fullscreen mode Exit fullscreen mode

Handle using try/catch, matching on the exception:

try {
  c()
} catch {
| MyException(reason) => Js.log("MyException:" ++ reason)
}
Enter fullscreen mode Exit fullscreen mode

Handle using switch, matching on the exception.

Notice the need for the exception keyword here.

switch c() {
| exception MyException(reason) => Js.log("MyException:" ++ reason)
| () => ()
}
Enter fullscreen mode Exit fullscreen mode

Handle using switch and try detect if it's a JavaScript error:

switch c() {
| exception error => {
    // error: exn
    let optError: option<Js.Exn.t> = Js.Exn.asJsExn(error)
    switch optError {
    | Some(error) => {
        // error: Js.Exn.t
        Js.log(Js.Exn.name(error))
        Js.log(Js.Exn.message(error))
      }
    | None => Js.log("Not a JavaScript error")
    }
  }
| () => ()
}
Enter fullscreen mode Exit fullscreen mode

This prints "Not a JavaScript error", as expected

Handling promise rejections from JavaScript

Write a JavaScript function that returns a rejected promise, and add a binding for it.

%%raw(`
function d() {
  return Promise.reject(new Error("An error occurred"))
}
`)

@bs.val external d: unit => Js.Promise.t<unit> = "d"
Enter fullscreen mode Exit fullscreen mode

Catch the error and attempt to identify that it's a JavaScript error.

d()->Js.Promise.catch((error: Js.Promise.error) => {
  switch error {
  | exception error => {
      let optError: option<Js.Exn.t> = Js.Exn.asJsExn(error)
      switch optError {
      | Some(error) => {
          Js.log(Js.Exn.name(error))
          Js.log(Js.Exn.message(error))
        }
      | None => Js.log("Not a Js.Exn.t")
      }
    }
  | _ => Js.log("Not an exn")
  }
  Js.Promise.resolve()
}, _)
Enter fullscreen mode Exit fullscreen mode

This prints:

Not an exn
Enter fullscreen mode Exit fullscreen mode

Notes:

  • The JavaScript error is converted to type Js.Promise.error, which is not a member of the exn type.
  • The switch case exception error match does not occur, because the error is not an exn type.

Handling JavaScript promise rejections from ReScript

Write a function that returns a rejected promise containing a JavaScript error.

let e = (): Js.Promise.t<unit> => {
  Js.Promise.make((~resolve as _, ~reject as _) => {
    Js.Exn.raiseError("An error occurred")
  })
}
Enter fullscreen mode Exit fullscreen mode

Note: Raising the error has the effect of rejecting the promise.

Catch the error and try detect that it's a JavaScript error.

e()->Js.Promise.catch((error: Js.Promise.error) => {
  switch error {
  | exception error => {
      let optError: option<Js.Exn.t> = Js.Exn.asJsExn(error)
      switch optError {
      | Some(error) => {
          Js.log(Js.Exn.name(error))
          Js.log(Js.Exn.message(error))
        }
      | None => Js.log("Not a Js.Exn.t")
      }
    }
  | _ => Js.log("Not an exn")
  }
  Js.Promise.resolve()
}, _)
Enter fullscreen mode Exit fullscreen mode

This prints:

Not an exn
Enter fullscreen mode Exit fullscreen mode

Notes:

  • The JavaScript error is converted to type Js.Promise.error, which is not a member of the exn type.
  • The switch case exception error match does not occur, because the error is not an exn type.

Handling ReScript promise rejections from ReScript

Write a function that returns a rejected promise containing a Rescript error.

exception MyException(string)

let f = (): Js.Promise.t<unit> => {
  Js.Promise.make((~resolve as _, ~reject as _) => {
    raise(MyException("An error occurred"))
  })
}
Enter fullscreen mode Exit fullscreen mode

Note: Raising the error has the effect of rejecting the promise.

Catch the error and attempt to determine its type:

f()->Js.Promise.catch((error: Js.Promise.error) => {
  switch error {
  | exception error => {
      let optError: option<Js.Exn.t> = Js.Exn.asJsExn(error)
      switch optError {
      | Some(error) => {
          Js.log(Js.Exn.name(error))
          Js.log(Js.Exn.message(error))
        }
      | None => Js.log("Not a Js.Exn.t")
      }
    }
  | _ => Js.log("Not an exn")
  }
  Js.Promise.resolve()
}, _)
Enter fullscreen mode Exit fullscreen mode

This prints:

Not an exn
Enter fullscreen mode Exit fullscreen mode

Notes:

  • The ReScript error is converted to type Js.Promise.error, which is not a member of the exn type.
  • The switch case exception error match does not occur, because the error is not an exn type.

Handling ReScript promise rejections using the exn Failure variant.

The Failure(string) variant has an exn type.

Write a function that rejects a promise using a Failure:

let g = (): Js.Promise.t<unit> => {
  Js.Promise.make((~resolve as _, ~reject) => {
    reject(. Failure("An error occurred"))
  })
}
Enter fullscreen mode Exit fullscreen mode

Catch the error and attempt to determine its type:

g()->Js.Promise.catch((error: Js.Promise.error) => {
  switch error {
  | exception error => {
      let optError: option<Js.Exn.t> = Js.Exn.asJsExn(error)
      switch optError {
      | Some(error) => {
          Js.log(Js.Exn.name(error))
          Js.log(Js.Exn.message(error))
        }
      | None => Js.log("Not a Js.Exn.t")
      }
    }
  | _ => Js.log("Not an exn")
  }
  Js.Promise.resolve()
}, _)
Enter fullscreen mode Exit fullscreen mode

This prints:

Not an exn
Enter fullscreen mode Exit fullscreen mode

Notes:

  • The Failure error is converted to type Js.Promise.error, which is not a member of the exn type.
  • The switch case exception error match does not occur, because the error is not an exn type.

References

💖 💪 🙅 🚩
kevanstannard
Kevan Stannard

Posted on November 21, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related