diff options
author | Alexander Ivanov <alehander42@gmail.com> | 2017-12-19 01:34:55 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2017-12-19 00:34:54 +0100 |
commit | a9ba02e8c90942ac3bc38c4fbab152968a8147dc (patch) | |
tree | 9ce420f1daec5efaf971e2547363d13969704abd | |
parent | a879973081e2c29d64e9fb9d8e539aa980533b10 (diff) | |
download | Nim-a9ba02e8c90942ac3bc38c4fbab152968a8147dc.tar.gz |
added asyncjs standard library module (#6841)
-rw-r--r-- | changelog.md | 18 | ||||
-rw-r--r-- | doc/lib.rst | 2 | ||||
-rw-r--r-- | lib/js/asyncjs.nim | 110 | ||||
-rw-r--r-- | tests/js/tasync.nim | 26 | ||||
-rw-r--r-- | web/website.ini | 2 |
5 files changed, 150 insertions, 8 deletions
diff --git a/changelog.md b/changelog.md index b216b0f17..d519ecfcf 100644 --- a/changelog.md +++ b/changelog.md @@ -39,36 +39,37 @@ what to return if the environment variable does not exist. - Bodies of ``for`` loops now get their own scope: -.. code-block:: nim +```nim # now compiles: for i in 0..4: let i = i + 1 echo i +``` - The parsing rules of ``if`` expressions were changed so that multiple statements are allowed in the branches. We found few code examples that now fail because of this change, but here is one: -.. code-block:: nim - +```nim t[ti] = if exp_negative: '-' else: '+'; inc(ti) +``` This now needs to be written as: -.. code-block:: nim - +```nim t[ti] = (if exp_negative: '-' else: '+'); inc(ti) +``` - To make Nim even more robust the system iterators ``..`` and ``countup`` now only accept a single generic type ``T``. This means the following code doesn't die with an "out of range" error anymore: -.. code-block:: nim - +```nim var b = 5.Natural var a = -5 for i in a..b: echo i +``` - ``formatFloat``/``formatBiggestFloat`` now support formatting floats with zero 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 called [strformat](https://nim-lang.org/docs/strformat.html) has been added 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. diff --git a/doc/lib.rst b/doc/lib.rst index 6eaf6c788..2719472fe 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -433,6 +433,8 @@ Modules for JS backend * `jsffi <jsffi.html>`_ Types and macros for easier interaction with JavaScript. +* `asyncjs <asyncjs.html>`_ + Types and macros for writing asynchronous procedures in JavaScript. Deprecated modules ------------------ diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim new file mode 100644 index 000000000..bde3d787f --- /dev/null +++ b/lib/js/asyncjs.nim @@ -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 diff --git a/tests/js/tasync.nim b/tests/js/tasync.nim new file mode 100644 index 000000000..a164827d2 --- /dev/null +++ b/tests/js/tasync.nim @@ -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) + diff --git a/web/website.ini b/web/website.ini index 5560e67ea..d8deb2d70 100644 --- a/web/website.ini +++ b/web/website.ini @@ -67,7 +67,7 @@ srcdoc2: "pure/collections/heapqueue" srcdoc2: "pure/fenv;impure/rdstdin;pure/strformat" srcdoc2: "pure/segfaults" 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 ; should live here |