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.

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

should be equivalent to

  1. async function loadGame(name) {
  2. // code
  3. }

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

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

  1. proc loadGame(name: string): Future[Game] =
  2. var promise = newPromise() do (resolve: proc(response: Game)):
  3. cbBasedLoadGame(name) do (game: Game):
  4. resolve(game)
  5. return promise

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

  1. 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

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

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

  1. Future[T] = ref object
  2. future*: T

Source Edit

  1. OnReject = proc (reason: Error)

Source Edit

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

Source Edit

Procs

  1. 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

  1. from std/sugar import `=>`
  2. from std/strutils import contains
  3. proc fn(n: int): Future[int] {.async.} =
  4. if n >= 7: raise newException(ValueError, "foobar: " & $n)
  5. else: result = n * 2
  6. proc main() {.async.} =
  7. var reason: Error
  8. await fn(6).catch((r: Error) => (reason = r)) # note: `()` are needed, `=> reason = r` would not work
  9. assert reason == nil
  10. await fn(7).catch((r: Error) => (reason = r))
  11. assert reason != nil
  12. assert "foobar: 7" in $reason.message
  13. discard main()

Source Edit

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

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

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

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

  1. 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

  1. from std/sugar import `=>`
  2. proc fn(n: int): Future[int] {.async.} =
  3. if n >= 7: raise newException(ValueError, "foobar: " & $n)
  4. else: result = n * 2
  5. proc asyncFact(n: int): Future[int] {.async.} =
  6. if n > 0: result = n * await asyncFact(n-1)
  7. else: result = 1
  8. proc main() {.async.} =
  9. block: # then
  10. assert asyncFact(3).await == 3*2
  11. assert asyncFact(3).then(asyncFact).await == 6*5*4*3*2
  12. let x1 = await fn(3)
  13. assert x1 == 3 * 2
  14. let x2 = await fn(4)
  15. .then((a: int) => a.float)
  16. .then((a: float) => $a)
  17. assert x2 == "8.0"
  18. block: # then with `onReject` callback
  19. var witness = 1
  20. await fn(6).then((a: int) => (witness = 2), (r: Error) => (witness = 3))
  21. assert witness == 2
  22. await fn(7).then((a: int) => (witness = 2), (r: Error) => (witness = 3))
  23. assert witness == 3

Source Edit

Macros

  1. macro async(arg: untyped): untyped

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