diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/js/asyncjs.nim | 79 |
1 files changed, 58 insertions, 21 deletions
diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim index b42ce45d8..73af232b4 100644 --- a/lib/js/asyncjs.nim +++ b/lib/js/asyncjs.nim @@ -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. +# xxx code-block:: javascript above gives `LanguageXNotSupported` warning. + 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 @@ -157,7 +158,17 @@ proc newPromise*(handler: proc(resolve: proc())): Future[void] {.importcpp: "(ne ## A helper for wrapping callback-based functions ## into promises and async procedures. +template typeOrVoid[T](a: T): type = + # xxx this is useful, make it public in std/typetraits in future work + T + +template maybeFuture(T): untyped = + # avoids `Future[Future[T]]` + when T is Future: T + else: Future[T] + when defined(nimExperimentalAsyncjsThen): + import std/private/since since (1, 5, 1): #[ TODO: @@ -177,44 +188,70 @@ when defined(nimExperimentalAsyncjsThen): type OnReject* = proc(reason: Error) - proc then*[T, T2](future: Future[T], onSuccess: proc(value: T): T2, onReject: OnReject = nil): Future[T2] = + 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 - asm "`result` = `future`.then(`onSuccess`, `onReject`)" + ## Returns a `Future` from the return type of `onSuccess(T.default)`. + runnableExamples("-d:nimExperimentalAsyncjsThen"): + from std/sugar import `=>` - 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 fn(n: int): Future[int] {.async.} = + if n >= 7: raise newException(ValueError, "foobar: " & $n) + else: result = n * 2 - 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 asyncFact(n: int): Future[int] {.async.} = + if n > 0: result = n * await asyncFact(n-1) + else: result = 1 - 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 main() {.async.} = + block: # then + assert asyncFact(3).await == 3*2 + assert asyncFact(3).then(asyncFact).await == 6*5*4*3*2 + 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" + + block: # then with `onReject` callback + var witness = 1 + await fn(6).then((a: int) => (witness = 2), (r: Error) => (witness = 3)) + assert witness == 2 + await fn(7).then((a: int) => (witness = 2), (r: Error) => (witness = 3)) + assert witness == 3 + + template impl(call): untyped = + when typeOrVoid(call) is void: + var ret: Future[void] + else: + var ret = default(maybeFuture(typeof(call))) + typeof(ret) + when T is void: + type A = impl(onSuccess()) + else: + type A = impl(onSuccess(default(T))) + var ret: A + asm "`ret` = `future`.then(`onSuccess`, `onReject`)" + return ret 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: + runnableExamples("-d:nimExperimentalAsyncjsThen"): 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" + proc main() {.async.} = var reason: Error - await fn(6).catch((r: Error) => (reason = r)) + await fn(6).catch((r: Error) => (reason = r)) # note: `()` are needed, `=> reason = r` would not work 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`)" |