|
|
|
<h2 id="coroutines">Coroutines</h2>
|
|
|
|
|
|
|
|
<p><b>(This section is incomplete.)</b></p>
|
|
|
|
|
|
|
|
<h3>Overview of coroutines</h3>
|
|
|
|
|
|
|
|
<p>Duktape has a support for simple coroutines. Execution is strictly nesting:
|
|
|
|
coroutine A resumes or initiates coroutine B, coroutine B runs until it yields
|
|
|
|
or finishes (either successfully or through an uncaught error), after which
|
|
|
|
coroutine A continues execution with the yield result.</p>
|
|
|
|
|
|
|
|
<p>Coroutines are created with <tt>__duk__.spawn()</tt>, which gets as its sole
|
|
|
|
argument the initial function where the new coroutine begins execution on its
|
|
|
|
first resume. The resume argument becomes the initial function's first (and
|
|
|
|
only) argument value.</p>
|
|
|
|
|
|
|
|
<p>A coroutine is resumed using <tt>__duk__.resume()</tt> which takes the
|
|
|
|
following arguments: the coroutine to resume, the resume value, and (optionally)
|
|
|
|
a flag indicating whether the resume value is an ordinary value or an error to
|
|
|
|
be injected into the target coroutine. Injecting an error means that the resume
|
|
|
|
value will be "thrown" at the site of the target coroutine's last yield operation.
|
|
|
|
In other words, instead of returning with an ordinary value, the yield will
|
|
|
|
seemingly throw an error.</p>
|
|
|
|
|
|
|
|
<p>A coroutine yields its current execution using <tt>__duk__.yield()</tt> which
|
|
|
|
takes as its arguments: the value to yield, and (optionally) a flag indicating
|
|
|
|
whether the yield value is an ordinary value or an error to be thrown in the
|
|
|
|
context of the resuming coroutine. In other words, an error value causes the
|
|
|
|
resume operation to seemingly throw an error instead of returning an ordinary
|
|
|
|
value.</p>
|
|
|
|
|
|
|
|
<p>If a coroutine exists successfully, i.e. the initial function finishes by
|
|
|
|
returning a value, it is handled similarly to a yield with the return value.
|
|
|
|
If a coroutine exists because of an uncaught error, it is handled similarly
|
|
|
|
to a yield with the error: the resume operation will rethrow that error in
|
|
|
|
the resuming coroutine's context. In either case the coroutine which has
|
|
|
|
finished can no longer be resumed; attempt to do so will cause a TypeError.</p>
|
|
|
|
|
|
|
|
<p>There are currently strict limitations on when resume/yield are possible.
|
|
|
|
See <a href="#limitations">Limitations</a>.</p>
|
|
|
|
|
|
|
|
<h3>Example</h3>
|
|
|
|
|
|
|
|
<p>A simple example of the basic mechanics of spawning, resuming, and yielding:</p>
|
|
|
|
<pre class="ecmascript-code">
|
|
|
|
// coroutine.js
|
|
|
|
function yielder(x) {
|
|
|
|
var yield = __duk__.yield;
|
|
|
|
|
|
|
|
print('yielder starting');
|
|
|
|
print('yielder arg:', x);
|
|
|
|
|
|
|
|
print('resumed with', yield(1));
|
|
|
|
print('resumed with', yield(2));
|
|
|
|
print('resumed with', yield(3));
|
|
|
|
|
|
|
|
print('yielder ending');
|
|
|
|
return 123;
|
|
|
|
}
|
|
|
|
|
|
|
|
var t = __duk__.spawn(yielder);
|
|
|
|
|
|
|
|
print('resume test');
|
|
|
|
print('yielded with', __duk__.resume(t, 'foo'));
|
|
|
|
print('yielded with', __duk__.resume(t, 'bar'));
|
|
|
|
print('yielded with', __duk__.resume(t, 'quux'));
|
|
|
|
print('yielded with', __duk__.resume(t, 'baz'));
|
|
|
|
print('finished');
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
<p>When executed with the <tt>duk</tt> command line tool, this prints:</p>
|
|
|
|
<pre>
|
|
|
|
$ duk coroutine.js
|
|
|
|
resume test
|
|
|
|
yielder starting
|
|
|
|
yielder arg: foo
|
|
|
|
yielded with 1
|
|
|
|
resumed with bar
|
|
|
|
yielded with 2
|
|
|
|
resumed with quux
|
|
|
|
yielded with 3
|
|
|
|
resumed with baz
|
|
|
|
yielder ending
|
|
|
|
yielded with 123
|
|
|
|
finished
|
|
|
|
</pre>
|
|
|
|
|