added asyncjs standard library module (#6841)

This commit is contained in:
Alexander Ivanov 2017-12-19 01:34:55 +02:00 • committed by Andreas Rumpf
commit a9ba02e8c9
5 changed files with 150 additions and 8 deletions

View file

@ -39,36 +39,37 @@
what to return if the environment variable does not exist. what to return if the environment variable does not exist.
- Bodies of ``for`` loops now get their own scope: - Bodies of ``for`` loops now get their own scope:
.. code-block:: nim ```nim
# now compiles: # now compiles:
for i in 0..4: for i in 0..4:
let i = i + 1 let i = i + 1
echo i echo i
```
- The parsing rules of ``if`` expressions were changed so that multiple - The parsing rules of ``if`` expressions were changed so that multiple
statements are allowed in the branches. We found few code examples that statements are allowed in the branches. We found few code examples that
now fail because of this change, but here is one: now fail because of this change, but here is one:
.. code-block:: nim ```nim
t[ti] = if exp_negative: '-' else: '+'; inc(ti) t[ti] = if exp_negative: '-' else: '+'; inc(ti)
```
This now needs to be written as: This now needs to be written as:
.. code-block:: nim ```nim
t[ti] = (if exp_negative: '-' else: '+'); inc(ti) t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
```
- To make Nim even more robust the system iterators ``..`` and ``countup`` - To make Nim even more robust the system iterators ``..`` and ``countup``
now only accept a single generic type ``T``. This means the following code now only accept a single generic type ``T``. This means the following code
doesn't die with an "out of range" error anymore: doesn't die with an "out of range" error anymore:
.. code-block:: nim ```nim
var b = 5.Natural var b = 5.Natural
var a = -5 var a = -5
for i in a..b: for i in a..b:
echo i echo i
```
- ``formatFloat``/``formatBiggestFloat`` now support formatting floats with zero - ``formatFloat``/``formatBiggestFloat`` now support formatting floats with zero
precision digits. The previous ``precision = 0`` behavior (default formatting) precision digits. The previous ``precision = 0`` behavior (default formatting)
@ -139,3 +140,6 @@ This now needs to be written as:
- For string formatting / interpolation a new module - For string formatting / interpolation a new module
called [strformat](https://nim-lang.org/docs/strformat.html) has been added called [strformat](https://nim-lang.org/docs/strformat.html) has been added
to the stdlib. to the stdlib.
- codegenDecl pragma now works for the JavaScript backend. It returns an empty string for
function return type placeholders.
- Asynchronous programming for the JavaScript backend using the `asyncjs` module.

View file

@ -433,6 +433,8 @@ Modules for JS backend
* `jsffi <jsffi.html>`_ * `jsffi <jsffi.html>`_
Types and macros for easier interaction with JavaScript. Types and macros for easier interaction with JavaScript.
* `asyncjs <asyncjs.html>`_
Types and macros for writing asynchronous procedures in JavaScript.
Deprecated modules Deprecated modules
------------------ ------------------

110
lib/js/asyncjs.nim Normal file
View file

@ -0,0 +1,110 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2017 Nim Authors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
## 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.
##
## .. code-block:: nim
## proc loadGame(name: string): Future[Game] {.async.} =
## # code
##
## should be equivalent to
##
## .. code-block:: javascript
## async function loadGame(name) {
## // code
## }
##
## A call to an asynchronous procedure usually needs ``await`` to wait for
## the completion of the ``Future``.
##
## .. code-block:: nim
## 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``:
##
## .. code-block:: nim
## 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 don't need to add the ``{.async.}`` pragma:
##
## .. code-block:: nim
## proc loadGame(name: string): Future[Game]
##
## 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.
import jsffi
import macros
when not defined(js) and not defined(nimdoc) and not defined(nimsuggest):
{.fatal: "Module asyncjs is designed to be used with the JavaScript backend.".}
type
Future*[T] = ref object
future*: T
## Wraps the return type of an asynchronous procedure.
PromiseJs* {.importcpp: "Promise".} = ref object
## A JavaScript Promise
proc replaceReturn(node: var NimNode) =
var z = 0
for s in node:
var son = node[z]
if son.kind == nnkReturnStmt:
node[z] = nnkReturnStmt.newTree(nnkCall.newTree(ident("jsResolve"), son[0]))
elif son.kind == nnkAsgn and son[0].kind == nnkIdent and $son[0] == "result":
node[z] = nnkAsgn.newTree(son[0], nnkCall.newTree(ident("jsResolve"), son[1]))
else:
replaceReturn(son)
inc z
proc generateJsasync(arg: NimNode): NimNode =
assert arg.kind == nnkProcDef
result = arg
if arg.params[0].kind == nnkEmpty:
result.params[0] = nnkBracketExpr.newTree(ident("Future"), ident("void"))
var code = result.body
replaceReturn(code)
result.body = nnkStmtList.newTree()
var q = quote:
proc await[T](f: Future[T]): T {.importcpp: "(await #)".}
proc jsResolve[T](a: T): Future[T] {.importcpp: "#".}
result.body.add(q)
for child in code:
result.body.add(child)
result.pragma = quote:
{.codegenDecl: "async function $2($3)".}
macro async*(arg: untyped): untyped =
## Macro which converts normal procedures into
## javascript-compatible async procedures
generateJsasync(arg)
proc newPromise*[T](handler: proc(resolve: proc(response: T))): Future[T] {.importcpp: "(new Promise(#))".}
## A helper for wrapping callback-based functions
## into promises and async procedures

26
tests/js/tasync.nim Normal file
View file

@ -0,0 +1,26 @@
discard """
disabled: true
output: '''
0
x
'''
"""
import asyncjs
# demonstrate forward definition
# for js
proc y(e: int): Future[string]
proc x(e: int) {.async.} =
var s = await y(e)
echo s
proc y(e: int): Future[string] {.async.} =
echo 0
return "x"
discard x(2)

View file

@ -67,7 +67,7 @@ srcdoc2: "pure/collections/heapqueue"
srcdoc2: "pure/fenv;impure/rdstdin;pure/strformat" srcdoc2: "pure/fenv;impure/rdstdin;pure/strformat"
srcdoc2: "pure/segfaults" srcdoc2: "pure/segfaults"
srcdoc2: "pure/basic2d;pure/basic3d;pure/mersenne;pure/coro;pure/httpcore" srcdoc2: "pure/basic2d;pure/basic3d;pure/mersenne;pure/coro;pure/httpcore"
srcdoc2: "pure/bitops;pure/nimtracker;pure/punycode;pure/volatile" srcdoc2: "pure/bitops;pure/nimtracker;pure/punycode;pure/volatile;js/asyncjs"
; Note: everything under 'webdoc' doesn't get listed in the index, so wrappers ; Note: everything under 'webdoc' doesn't get listed in the index, so wrappers
; should live here ; should live here