☰

# std/asyncjs

Theme:

- Manual
- Standard library
- Index
- Compiler docs
- Fusion docs
- devel, stable

Search:
Source Edit

This module implements types and macros for writing asynchronous code for the JS backend. It provides tools for interaction with JavaScript async API-s and libraries, writing async procedures in Nim and converting callback-based code to promises.

A Nim procedure is asynchronous when it includes the `{.async.}` pragma. It should always have a `Future[T]` return type or not have a return type at all. A `Future[void]` return type is assumed by default.

This is roughly equivalent to the `async` keyword in JavaScript code.

    proc loadGame(name: string): Future[Game] {.async.} =
      # code

should be equivalent to

    async function loadGame(name) {
      // code
    }

A call to an asynchronous procedure usually needs `await` to wait for the completion of the `Future`.

    var game = await loadGame(name)

Often, you might work with callback-based API-s. You can wrap them with asynchronous procedures using promises and `newPromise`:

    proc loadGame(name: string): Future[Game] =
      var promise = newPromise() do (resolve: proc(response: Game)):
        cbBasedLoadGame(name) do (game: Game):
          resolve(game)
      return promise

Forward definitions work properly, you just need to always add the `{.async.}` pragma:

    proc loadGame(name: string): Future[Game] {.async.}

# JavaScript compatibility

Nim currently generates `async/await` JavaScript code which is supported in modern EcmaScript and most modern versions of browsers, Node.js and Electron. If you need to use this module with older versions of JavaScript, you can use a tool that backports the resulting JavaScript code, as babel.

# Imports

jsffi, macros, since

# Types

    Error {.importjs: "Error".} = ref object of JsRoot
      message*: cstring
      name*: cstring

  https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error Source Edit

    Future[T] = ref object
      future*: T

  Source Edit

    OnReject = proc (reason: Error)

  Source Edit

    PromiseJs {.importjs: "Promise".} = ref object

  Source Edit

# Procs

    proc catch[T](future: Future[T]; onReject: OnReject): Future[void]

  See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch

  Example: cmd: -r:off

      from std/sugar import `=>`
      from std/strutils import contains

      proc fn(n: int): Future[int] {.async.} =
        if n >= 7: raise newException(ValueError, "foobar: " & $n)
        else: result = n * 2

      proc main() {.async.} =
        var reason: Error
        await fn(6).catch((r: Error) => (reason = r)) # note: `()` are needed, `=> reason = r` would not work
        assert reason == nil
        await fn(7).catch((r: Error) => (reason = r))
        assert reason != nil
        assert  "foobar: 7" in $reason.message

      discard main()

  Source Edit

    proc newPromise(handler: proc (resolve: proc ())): Future[void] {.
        importjs: "(new Promise(#))", ...raises: [], tags: [], forbids: [].}

  A helper for wrapping callback-based functions into promises and async procedures. Source Edit

    proc newPromise[T](handler: proc (resolve: proc (response: T))): Future[T] {.
        importjs: "(new Promise(#))", ...raises: [], tags: [], forbids: [].}

  A helper for wrapping callback-based functions into promises and async procedures. Source Edit

    proc then[T](future: Future[T]; onSuccess: proc; onReject: OnReject = nil): auto

  See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then Returns a `Future` from the return type of `onSuccess(T.default)`.

  Example: cmd: -r:off

      from std/sugar import `=>`

      proc fn(n: int): Future[int] {.async.} =
        if n >= 7: raise newException(ValueError, "foobar: " & $n)
        else: result = n * 2

      proc asyncFact(n: int): Future[int] {.async.} =
        if n > 0: result = n * await asyncFact(n-1)
        else: result = 1

      proc main() {.async.} =
        block: # then
          assert asyncFact(3).await == 3*2
          assert asyncFact(3).then(asyncFact).await == 6*5*4*3*2
          let x1 = await fn(3)
          assert x1 == 3 * 2
          let x2 = await fn(4)
            .then((a: int) => a.float)
            .then((a: float) => $a)
          assert x2 == "8.0"

        block: # then with `onReject` callback
          var witness = 1
          await fn(6).then((a: int) => (witness = 2), (r: Error) => (witness = 3))
          assert witness == 2
          await fn(7).then((a: int) => (witness = 2), (r: Error) => (witness = 3))
          assert witness == 3

  Source Edit

# Macros

    macro async(arg: untyped): untyped

  Macro which converts normal procedures into javascript-compatible async procedures. Source Edit