added asyncjs standard library module (#6841)
This commit is contained in:
parent
a879973081
commit
a9ba02e8c9
5 changed files with 150 additions and 8 deletions
18
changelog.md
18
changelog.md
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
110
lib/js/asyncjs.nim
Normal 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
26
tests/js/tasync.nim
Normal 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)
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue