Avoid recursion during tail calls.

An asynchronous task that finishes would previously trigger recursive invocation
of subsequent synchronous tasks. Now these tasks are handled in a loop instead.