summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/asyncdispatch.nim63
-rw-r--r--lib/pure/asyncmacro.nim4
-rw-r--r--lib/upcoming/asyncdispatch.nim33
3 files changed, 75 insertions, 25 deletions
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 802c40495..06c21e08d 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -105,6 +105,36 @@ export Port, SocketFlag
 ## be used to await a procedure returning a ``Future[void]``:
 ## ``await socket.send("foobar")``.
 ##
+## If an awaited future completes with an error, then ``await`` will re-raise
+## this error. To avoid this, you can use the ``yield`` keyword instead of
+## ``await``. The following section shows different ways that you can handle
+## exceptions in async procs.
+##
+## Handling Exceptions
+## ~~~~~~~~~~~~~~~~~~~
+##
+## The most reliable way to handle exceptions is to use ``yield`` on a future
+## then check the future's ``failed`` property. For example:
+##
+##   .. code-block:: Nim
+##     var future = sock.recv(100)
+##     yield future
+##     if future.failed:
+##       # Handle exception
+##
+## The ``async`` procedures also offer limited support for the try statement.
+##
+##    .. code-block:: Nim
+##      try:
+##        let data = await sock.recv(100)
+##        echo("Received ", data)
+##      except:
+##        # Handle exception
+##
+## Unfortunately the semantics of the try statement may not always be correct,
+## and occassionally the compilation may fail altogether.
+## As such it is better to use the former style when possible.
+##
 ## Discarding futures
 ## ------------------
 ##
@@ -339,17 +369,22 @@ proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
   var retFuture = newFuture[void]("asyncdispatch.`and`")
   fut1.callback =
     proc () =
-      if fut2.finished: retFuture.complete()
+      if not retFuture.finished:
+        if fut1.failed: retFuture.fail(fut1.error)
+        elif fut2.finished: retFuture.complete()
   fut2.callback =
     proc () =
-      if fut1.finished: retFuture.complete()
+      if not retFuture.finished:
+        if fut2.failed: retFuture.fail(fut2.error)
+        elif fut1.finished: retFuture.complete()
   return retFuture
 
 proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
   ## Returns a future which will complete once either ``fut1`` or ``fut2``
   ## complete.
   var retFuture = newFuture[void]("asyncdispatch.`or`")
-  proc cb() =
+  proc cb(fut: Future[T]) =
+    if fut.failed: retFuture.fail(fut.error)
     if not retFuture.finished: retFuture.complete()
   fut1.callback = cb
   fut2.callback = cb
@@ -374,10 +409,13 @@ proc all*[T](futs: varargs[Future[T]]): auto =
 
     for fut in futs:
       fut.callback = proc(f: Future[T]) =
-        inc(completedFutures)
+        if f.failed:
+          retFuture.fail(f.error)
+        elif not retFuture.finished:
+          inc(completedFutures)
 
-        if completedFutures == totalFutures:
-          retFuture.complete()
+          if completedFutures == totalFutures:
+            retFuture.complete()
 
     return retFuture
 
@@ -390,11 +428,14 @@ proc all*[T](futs: varargs[Future[T]]): auto =
     for i, fut in futs:
       proc setCallback(i: int) =
         fut.callback = proc(f: Future[T]) =
-          retValues[i] = f.read()
-          inc(completedFutures)
-
-          if completedFutures == len(retValues):
-            retFuture.complete(retValues)
+          if f.failed:
+            retFuture.fail(f.error)
+          elif not retFuture.finished:
+            retValues[i] = f.read()
+            inc(completedFutures)
+
+            if completedFutures == len(retValues):
+              retFuture.complete(retValues)
 
       setCallback(i)
 
diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim
index 28e3e2a16..6b565cd3b 100644
--- a/lib/pure/asyncmacro.nim
+++ b/lib/pure/asyncmacro.nim
@@ -144,11 +144,9 @@ proc processBody(node, retFutureSym: NimNode,
   of nnkCommand, nnkCall:
     if node[0].kind == nnkIdent and node[0].ident == !"await":
       case node[1].kind
-      of nnkIdent, nnkInfix, nnkDotExpr:
+      of nnkIdent, nnkInfix, nnkDotExpr, nnkCall, nnkCommand:
         # await x
         # await x or y
-        result = newNimNode(nnkYieldStmt, node).add(node[1]) # -> yield x
-      of nnkCall, nnkCommand:
         # await foo(p, x)
         # await foo p, x
         var futureValue: NimNode
diff --git a/lib/upcoming/asyncdispatch.nim b/lib/upcoming/asyncdispatch.nim
index 689d0272c..460dfbd1a 100644
--- a/lib/upcoming/asyncdispatch.nim
+++ b/lib/upcoming/asyncdispatch.nim
@@ -339,17 +339,22 @@ proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
   var retFuture = newFuture[void]("asyncdispatch.`and`")
   fut1.callback =
     proc () =
-      if fut2.finished: retFuture.complete()
+      if not retFuture.finished:
+        if fut1.failed: retFuture.fail(fut1.error)
+        elif fut2.finished: retFuture.complete()
   fut2.callback =
     proc () =
-      if fut1.finished: retFuture.complete()
+      if not retFuture.finished:
+        if fut2.failed: retFuture.fail(fut2.error)
+        elif fut1.finished: retFuture.complete()
   return retFuture
 
 proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
   ## Returns a future which will complete once either ``fut1`` or ``fut2``
   ## complete.
   var retFuture = newFuture[void]("asyncdispatch.`or`")
-  proc cb() =
+  proc cb(fut: Future[T]) =
+    if fut.failed: retFuture.fail(fut.error)
     if not retFuture.finished: retFuture.complete()
   fut1.callback = cb
   fut2.callback = cb
@@ -374,10 +379,13 @@ proc all*[T](futs: varargs[Future[T]]): auto =
 
     for fut in futs:
       fut.callback = proc(f: Future[T]) =
-        inc(completedFutures)
+        if f.failed:
+          retFuture.fail(f.error)
+        elif not retFuture.finished:
+          inc(completedFutures)
 
-        if completedFutures == totalFutures:
-          retFuture.complete()
+          if completedFutures == totalFutures:
+            retFuture.complete()
 
     return retFuture
 
@@ -390,11 +398,14 @@ proc all*[T](futs: varargs[Future[T]]): auto =
     for i, fut in futs:
       proc setCallback(i: int) =
         fut.callback = proc(f: Future[T]) =
-          retValues[i] = f.read()
-          inc(completedFutures)
-
-          if completedFutures == len(retValues):
-            retFuture.complete(retValues)
+          if f.failed:
+            retFuture.fail(f.error)
+          elif not retFuture.finished:
+            retValues[i] = f.read()
+            inc(completedFutures)
+
+            if completedFutures == len(retValues):
+              retFuture.complete(retValues)
 
       setCallback(i)