asyncjs: add then, catch for promise pipelining (#16871)

* asyncjs: add then
* improve tests, changelog, API
* fix cryptic windows error: The parameter is incorrect
* address comments
This commit is contained in:
Timothee Cour
2021-02-24 12:03:21 -08:00
committed by GitHub
parent 99633d7682
commit a4e6b242d5
7 changed files with 166 additions and 24 deletions

View File

@@ -57,12 +57,13 @@
## 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 std/jsffi
import std/macros
when not defined(js) and not defined(nimsuggest):
{.fatal: "Module asyncjs is designed to be used with the JavaScript backend.".}
import std/jsffi
import std/macros
import std/private/since
type
Future*[T] = ref object
future*: T
@@ -154,3 +155,65 @@ proc newPromise*[T](handler: proc(resolve: proc(response: T))): Future[T] {.impo
proc newPromise*(handler: proc(resolve: proc())): Future[void] {.importcpp: "(new Promise(#))".}
## A helper for wrapping callback-based functions
## into promises and async procedures.
when defined(nimExperimentalAsyncjsThen):
since (1, 5, 1):
#[
TODO:
* map `Promise.all()`
* proc toString*(a: Error): cstring {.importjs: "#.toString()".}
Note:
We probably can't have a `waitFor` in js in browser (single threaded), but maybe it would be possible
in in nodejs, see https://nodejs.org/api/child_process.html#child_process_child_process_execsync_command_options
and https://stackoverflow.com/questions/61377358/javascript-wait-for-async-call-to-finish-before-returning-from-function-witho
]#
type Error* {.importjs: "Error".} = ref object of JsRoot
## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
message*: cstring
name*: cstring
type OnReject* = proc(reason: Error)
proc then*[T, T2](future: Future[T], onSuccess: proc(value: T): T2, onReject: OnReject = nil): Future[T2] =
## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
asm "`result` = `future`.then(`onSuccess`, `onReject`)"
proc then*[T](future: Future[T], onSuccess: proc(value: T), onReject: OnReject = nil): Future[void] =
## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
asm "`result` = `future`.then(`onSuccess`, `onReject`)"
proc then*(future: Future[void], onSuccess: proc(), onReject: OnReject = nil): Future[void] =
## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
asm "`result` = `future`.then(`onSuccess`, `onReject`)"
proc then*[T2](future: Future[void], onSuccess: proc(): T2, onReject: OnReject = nil): Future[T2] =
## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
asm "`result` = `future`.then(`onSuccess`, `onReject`)"
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
runnableExamples:
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.} =
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"
var reason: Error
await fn(6).catch((r: Error) => (reason = r))
assert reason == nil
await fn(7).catch((r: Error) => (reason = r))
assert reason != nil
assert "foobar: 7" in $reason.message
discard main()
asm "`result` = `future`.catch(`onReject`)"