* followup #16871 asyncjs.then: allow pipelining procs returning futures * rename test files where they belong * fix tests * tests for then with `onReject` callback * rename test file containing fail to avoid messing with grep * address comments * cleanup * un-disable 1 test
This commit is contained in:
parent
5670b848cb
commit
a66637bda4
4 changed files with 87 additions and 28 deletions
|
|
@ -57,12 +57,13 @@
|
||||||
## If you need to use this module with older versions of JavaScript, you can
|
## 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.
|
## 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):
|
when not defined(js) and not defined(nimsuggest):
|
||||||
{.fatal: "Module asyncjs is designed to be used with the JavaScript backend.".}
|
{.fatal: "Module asyncjs is designed to be used with the JavaScript backend.".}
|
||||||
|
|
||||||
import std/jsffi
|
import std/jsffi
|
||||||
import std/macros
|
import std/macros
|
||||||
import std/private/since
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Future*[T] = ref object
|
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
|
## A helper for wrapping callback-based functions
|
||||||
## into promises and async procedures.
|
## 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):
|
when defined(nimExperimentalAsyncjsThen):
|
||||||
|
import std/private/since
|
||||||
since (1, 5, 1):
|
since (1, 5, 1):
|
||||||
#[
|
#[
|
||||||
TODO:
|
TODO:
|
||||||
|
|
@ -177,44 +188,70 @@ when defined(nimExperimentalAsyncjsThen):
|
||||||
|
|
||||||
type OnReject* = proc(reason: Error)
|
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
|
## 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"):
|
||||||
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/sugar import `=>`
|
||||||
from std/strutils import contains
|
|
||||||
proc fn(n: int): Future[int] {.async.} =
|
proc fn(n: int): Future[int] {.async.} =
|
||||||
if n >= 7: raise newException(ValueError, "foobar: " & $n)
|
if n >= 7: raise newException(ValueError, "foobar: " & $n)
|
||||||
else: result = n * 2
|
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 asyncFact(n: int): Future[int] {.async.} =
|
||||||
|
if n > 0: result = n * await asyncFact(n-1)
|
||||||
|
else: result = 1
|
||||||
|
|
||||||
|
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("-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.} =
|
||||||
var reason: Error
|
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
|
assert reason == nil
|
||||||
await fn(7).catch((r: Error) => (reason = r))
|
await fn(7).catch((r: Error) => (reason = r))
|
||||||
assert reason != nil
|
assert reason != nil
|
||||||
assert "foobar: 7" in $reason.message
|
assert "foobar: 7" in $reason.message
|
||||||
|
|
||||||
discard main()
|
discard main()
|
||||||
|
|
||||||
asm "`result` = `future`.catch(`onReject`)"
|
asm "`result` = `future`.catch(`onReject`)"
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,14 @@ proc fn(n: int): Future[int] {.async.} =
|
||||||
else:
|
else:
|
||||||
return 10
|
return 10
|
||||||
|
|
||||||
|
proc asyncFact(n: int): Future[int] {.async.} =
|
||||||
|
if n > 0: result = n * await asyncFact(n-1)
|
||||||
|
else: result = 1
|
||||||
|
|
||||||
|
proc asyncIdentity(n: int): Future[int] {.async.} =
|
||||||
|
if n > 0: result = 1 + await asyncIdentity(n-1)
|
||||||
|
else: result = 0
|
||||||
|
|
||||||
proc main() {.async.} =
|
proc main() {.async.} =
|
||||||
block: # then
|
block: # then
|
||||||
let x = await fn(4)
|
let x = await fn(4)
|
||||||
|
|
@ -63,6 +71,18 @@ proc main() {.async.} =
|
||||||
let x2 = await fn(4).then((a: int) => (discard)).then(() => 13)
|
let x2 = await fn(4).then((a: int) => (discard)).then(() => 13)
|
||||||
doAssert x2 == 13
|
doAssert x2 == 13
|
||||||
|
|
||||||
|
let x4 = await asyncFact(3).then(asyncIdentity).then(asyncIdentity).then((a:int) => a * 7).then(asyncIdentity)
|
||||||
|
doAssert x4 == 3 * 2 * 7
|
||||||
|
|
||||||
|
block: # bug #17177
|
||||||
|
proc asyncIdentityNested(n: int): Future[int] {.async.} = return n
|
||||||
|
let x5 = await asyncFact(3).then(asyncIdentityNested)
|
||||||
|
doAssert x5 == 3 * 2
|
||||||
|
|
||||||
|
when false: # xxx pending bug #17254
|
||||||
|
let x6 = await asyncFact(3).then((a:int) {.async.} => a * 11)
|
||||||
|
doAssert x6 == 3 * 2 * 11
|
||||||
|
|
||||||
block: # catch
|
block: # catch
|
||||||
var reason: Error
|
var reason: Error
|
||||||
await fn(6).then((a: int) => (witness.add $a)).catch((r: Error) => (reason = r))
|
await fn(6).then((a: int) => (witness.add $a)).catch((r: Error) => (reason = r))
|
||||||
|
|
@ -5,6 +5,8 @@ t
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# xxx merge into tasyncjs.nim
|
||||||
|
|
||||||
import asyncjs, macros
|
import asyncjs, macros
|
||||||
|
|
||||||
macro f*(a: untyped): untyped =
|
macro f*(a: untyped): untyped =
|
||||||
Loading…
Add table
Add a link
Reference in a new issue