Files
shiny/reference/ExtendedTask.html
2025-12-03 21:27:07 +00:00

297 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<!-- Generated by pkgdown: do not edit by hand --><html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Task or computation that proceeds in the background — ExtendedTask • shiny</title><!-- jquery --><script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script><!-- Bootstrap --><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha256-bZLfwXAP04zRMK2BjiO8iu9pf4FbLqX6zitd+tIvLhE=" crossorigin="anonymous"><script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha256-nuL8/2cJ5NDSSwnKD8VqreErSWHtnEP9E7AySL+1ev4=" crossorigin="anonymous"></script><!-- bootstrap-toc --><link rel="stylesheet" href="../bootstrap-toc.css"><script src="../bootstrap-toc.js"></script><!-- Font Awesome icons --><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/all.min.css" integrity="sha256-mmgLkCYLUQbXn0B1SRqzHar6dCnv9oZFPEC1g1cwlkk=" crossorigin="anonymous"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/v4-shims.min.css" integrity="sha256-wZjR52fzng1pJHwx4aV2AO3yyTOXrcDW7jBpJtTwVxw=" crossorigin="anonymous"><!-- clipboard.js --><script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.6/clipboard.min.js" integrity="sha256-inc5kl9MA1hkeYUt+EC3BhlIgyp/2jDIyBLS6k3UxPI=" crossorigin="anonymous"></script><!-- headroom.js --><script src="https://cdnjs.cloudflare.com/ajax/libs/headroom/0.11.0/headroom.min.js" integrity="sha256-AsUX4SJE1+yuDu5+mAVzJbuYNPHj/WroHuZ8Ir/CkE0=" crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/headroom/0.11.0/jQuery.headroom.min.js" integrity="sha256-ZX/yNShbjqsohH1k95liqY9Gd8uOiE1S4vZc+9KQ1K4=" crossorigin="anonymous"></script><!-- pkgdown --><link href="../pkgdown.css" rel="stylesheet"><script src="../pkgdown.js"></script><meta property="og:title" content="Task or computation that proceeds in the background — ExtendedTask"><meta property="og:description" content="In normal Shiny reactive code, whenever an observer, calc, or
output is busy computing, it blocks the current session from receiving any
inputs or attempting to proceed with any other computation related to that
session.
The ExtendedTask class allows you to have an expensive operation that is
started by a reactive effect, and whose (eventual) results can be accessed
by a regular observer, calc, or output; but during the course of the
operation, the current session is completely unblocked, allowing the user
to continue using the rest of the app while the operation proceeds in the
background.
Note that each ExtendedTask object does not represent a single
invocation of its long-running function. Rather, it's an object that is
used to invoke the function with different arguments, keeps track of
whether an invocation is in progress, and provides ways to get at the
current status or results of the operation. A single ExtendedTask object
does not permit overlapping invocations: if the invoke() method is called
before the previous invoke() is completed, the new invocation will not
begin until the previous invocation has completed."><meta property="og:image" content="/logo.png"><!-- mathjax --><script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js" integrity="sha256-nvJJv9wWKEm88qvoQl9ekL2J+k/RWIsaSScxxlsrv8k=" crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/config/TeX-AMS-MML_HTMLorMML.js" integrity="sha256-84DKXVJXs0/F8OTMzX4UR909+jtl4G7SPypPavF+GfA=" crossorigin="anonymous"></script><!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]--></head><body data-spy="scroll" data-target="#toc">
<div class="container template-reference-topic">
<header><div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<span class="navbar-brand">
<a class="navbar-link" href="../index.html">shiny</a>
<span class="version label label-default" data-toggle="tooltip" data-placement="bottom" title="">1.12.0.9000</span>
</span>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav"><li>
<a href="../reference/index.html">Reference</a>
</li>
<li>
<a href="../news/index.html">Changelog</a>
</li>
</ul><ul class="nav navbar-nav navbar-right"><li>
<a href="https://github.com/rstudio/shiny/" class="external-link">
<span class="fab fa-github fa-lg"></span>
</a>
</li>
</ul></div><!--/.nav-collapse -->
</div><!--/.container -->
</div><!--/.navbar -->
</header><div class="row">
<div class="col-md-9 contents">
<div class="page-header">
<h1>Task or computation that proceeds in the background</h1>
<small class="dont-index">Source: <a href="https://github.com/rstudio/shiny/blob/rc-v1.12.1/R/extended-task.R" class="external-link"><code>R/extended-task.R</code></a></small>
<div class="hidden name"><code>ExtendedTask.Rd</code></div>
</div>
<div class="ref-description">
<p>In normal Shiny reactive code, whenever an observer, calc, or
output is busy computing, it blocks the current session from receiving any
inputs or attempting to proceed with any other computation related to that
session.</p>
<p>The <code>ExtendedTask</code> class allows you to have an expensive operation that is
started by a reactive effect, and whose (eventual) results can be accessed
by a regular observer, calc, or output; but during the course of the
operation, the current session is completely unblocked, allowing the user
to continue using the rest of the app while the operation proceeds in the
background.</p>
<p>Note that each <code>ExtendedTask</code> object does not represent a <em>single
invocation</em> of its long-running function. Rather, it's an object that is
used to invoke the function with different arguments, keeps track of
whether an invocation is in progress, and provides ways to get at the
current status or results of the operation. A single <code>ExtendedTask</code> object
does not permit overlapping invocations: if the <code>invoke()</code> method is called
before the previous <code>invoke()</code> is completed, the new invocation will not
begin until the previous invocation has completed.</p>
</div>
<div id="extendedtask-versus-asynchronous-reactives">
<h2><code>ExtendedTask</code> versus asynchronous reactives</h2>
<p>Shiny has long supported <a href="https://rstudio.github.io/promises/articles/promises_06_shiny.html" class="external-link">using {promises}</a>
to write asynchronous observers, calcs, or outputs. You may be wondering
what the differences are between those techniques and this class.</p>
<p>Asynchronous observers, calcs, and outputs are notand have never
beendesigned to let a user start a long-running operation, while keeping
that very same (browser) session responsive to other interactions. Instead,
they unblock other sessions, so you can take a long-running operation that
would normally bring the entire R process to a halt and limit the blocking
to just the session that started the operation. (For more details, see the
section on <a href="https://rstudio.github.io/promises/articles/promises_06_shiny.html#the-flush-cycle" class="external-link">"The Flush Cycle"</a>.)</p>
<p><code>ExtendedTask</code>, on the other hand, invokes an asynchronous function (that
is, a function that quickly returns a promise) and allows even that very
session to immediately unblock and carry on with other user interactions.</p>
</div>
<div id="methods">
<h2>Methods</h2>
<div class="section">
<h3 id="public-methods">Public methods<a class="anchor" aria-label="anchor" href="#public-methods"></a></h3>
<ul><li><p><a href="#method-ExtendedTask-new"><code>ExtendedTask$new()</code></a></p></li>
<li><p><a href="#method-ExtendedTask-invoke"><code>ExtendedTask$invoke()</code></a></p></li>
<li><p><a href="#method-ExtendedTask-status"><code>ExtendedTask$status()</code></a></p></li>
<li><p><a href="#method-ExtendedTask-result"><code>ExtendedTask$result()</code></a></p></li>
</ul></div><p></p><hr><a id="method-ExtendedTask-new"></a><div class="section">
<h3 id="method-new-">Method <code><a href="https://rdrr.io/r/methods/new.html" class="external-link">new()</a></code><a class="anchor" aria-label="anchor" href="#method-new-"></a></h3>
<p>Creates a new <code>ExtendedTask</code> object. <code>ExtendedTask</code> should generally be
created either at the top of a server function, or at the top of a module
server function.</p><div class="section">
<h4 id="usage">Usage<a class="anchor" aria-label="anchor" href="#usage"></a></h4>
<p></p><div class="r"><div class="sourceCode"><pre><code><span><span class="va"><a href="../reference/ExtendedTask.html">ExtendedTask</a></span><span class="op">$</span><span class="fu">new</span><span class="op">(</span><span class="va">func</span><span class="op">)</span></span></code></pre></div><p></p></div>
</div>
<div class="section">
<h4 id="arguments">Arguments<a class="anchor" aria-label="anchor" href="#arguments"></a></h4>
<p></p><div class="arguments"><dl><dt><code>func</code></dt>
<dd><p>The long-running operation to execute. This should be an
asynchronous function, meaning, it should use the
<a href="https://rstudio.github.io/promises/" class="external-link">{promises}</a> package, most
likely in conjunction with the
<a href="https://mirai.r-lib.org" class="external-link">{mirai}</a> or
<a href="https://rstudio.github.io/promises/articles/promises_04_futures.html" class="external-link">{future}</a>
package. (In short, the return value of <code>func</code> should be a
<code><a href="https://mirai.r-lib.org/reference/mirai.html" class="external-link">mirai</a></code>, <code><a href="https://future.futureverse.org/reference/future.html" class="external-link">Future</a></code>, <code>promise</code>,
or something else that <code><a href="https://rstudio.github.io/promises/reference/is.promise.html" class="external-link">promises::as.promise()</a></code> understands.)</p>
<p>It's also important that this logic does not read from any
reactive inputs/sources, as inputs may change after the function is
invoked; instead, if the function needs to access reactive inputs, it
should take parameters and the caller of the <code>invoke()</code> method should
read reactive inputs and pass them as arguments.</p></dd>
</dl><p></p></div>
</div>
</div><p></p><hr><a id="method-ExtendedTask-invoke"></a><div class="section">
<h3 id="method-invoke-">Method <code>invoke()</code><a class="anchor" aria-label="anchor" href="#method-invoke-"></a></h3>
<p>Starts executing the long-running operation. If this <code>ExtendedTask</code> is
already running (meaning, a previous call to <code>invoke()</code> is not yet
complete) then enqueues this invocation until after the current
invocation, and any already-enqueued invocation, completes.</p><div class="section">
<h4 id="usage-1">Usage<a class="anchor" aria-label="anchor" href="#usage-1"></a></h4>
<p></p><div class="r"><div class="sourceCode"><pre><code><span><span class="va">ExtendedTask</span><span class="op">$</span><span class="fu">invoke</span><span class="op">(</span><span class="va">...</span><span class="op">)</span></span></code></pre></div><p></p></div>
</div>
<div class="section">
<h4 id="arguments-1">Arguments<a class="anchor" aria-label="anchor" href="#arguments-1"></a></h4>
<p></p><div class="arguments"><dl><dt><code>...</code></dt>
<dd><p>Parameters to use for this invocation of the underlying
function. If reactive inputs are needed by the underlying function,
they should be read by the caller of <code>invoke</code> and passed in as
arguments.</p></dd>
</dl><p></p></div>
</div>
</div><p></p><hr><a id="method-ExtendedTask-status"></a><div class="section">
<h3 id="method-status-">Method <code><a href="https://mirai.r-lib.org/reference/status.html" class="external-link">status()</a></code><a class="anchor" aria-label="anchor" href="#method-status-"></a></h3>
<p>This is a reactive read that invalidates the caller when the task's
status changes.</p>
<p>Returns one of the following values:</p><ul><li><p><code>"initial"</code>: This <code>ExtendedTask</code> has not yet been invoked</p></li>
<li><p><code>"running"</code>: An invocation is currently running</p></li>
<li><p><code>"success"</code>: An invocation completed successfully, and a value can be
retrieved via the <code>result()</code> method</p></li>
<li><p><code>"error"</code>: An invocation completed with an error, which will be
re-thrown if you call the <code>result()</code> method</p></li>
</ul><div class="section">
<h4 id="usage-2">Usage<a class="anchor" aria-label="anchor" href="#usage-2"></a></h4>
<p></p><div class="r"><div class="sourceCode"><pre><code><span><span class="va">ExtendedTask</span><span class="op">$</span><span class="fu">status</span><span class="op">(</span><span class="op">)</span></span></code></pre></div><p></p></div>
</div>
</div><p></p><hr><a id="method-ExtendedTask-result"></a><div class="section">
<h3 id="method-result-">Method <code>result()</code><a class="anchor" aria-label="anchor" href="#method-result-"></a></h3>
<p>Attempts to read the results of the most recent invocation. This is a
reactive read that invalidates as the task's status changes.</p>
<p>The actual behavior differs greatly depending on the current status of
the task:</p><ul><li><p><code>"initial"</code>: Throws a silent error (like <code><a href="req.html">req(FALSE)</a></code>). If
this happens during output rendering, the output will be blanked out.</p></li>
<li><p><code>"running"</code>: Throws a special silent error that, if it happens during
output rendering, makes the output appear "in progress" until further
notice.</p></li>
<li><p><code>"success"</code>: Returns the return value of the most recent invocation.</p></li>
<li><p><code>"error"</code>: Throws whatever error was thrown by the most recent
invocation.</p></li>
</ul><p>This method is intended to be called fairly naively by any output or
reactive expression that cares about the outputyou just have to be
aware that if the result isn't ready for whatever reason, processing will
stop in much the same way as <code>req(FALSE)</code> does, but when the result is
ready you'll get invalidated, and when you run again the result should be
there.</p>
<p>Note that the <code>result()</code> method is generally not meant to be used with
<code><a href="observeEvent.html">observeEvent()</a></code>, <code><a href="observeEvent.html">eventReactive()</a></code>, <code><a href="bindEvent.html">bindEvent()</a></code>, or <code><a href="isolate.html">isolate()</a></code> as the
invalidation will be ignored.</p><div class="section">
<h4 id="usage-3">Usage<a class="anchor" aria-label="anchor" href="#usage-3"></a></h4>
<p></p><div class="r"><div class="sourceCode"><pre><code><span><span class="va">ExtendedTask</span><span class="op">$</span><span class="fu">result</span><span class="op">(</span><span class="op">)</span></span></code></pre></div><p></p></div>
</div>
</div>
</div>
<div id="ref-examples">
<h2>Examples</h2>
<div class="sourceCode"><pre class="sourceCode r"><code><span class="r-in"><span><span class="kw">if</span> <span class="op">(</span><span class="cn">FALSE</span><span class="op">)</span> <span class="op">{</span> <span class="co"># rlang::is_interactive() &amp;&amp; rlang::is_installed("mirai")</span></span></span>
<span class="r-in"><span><span class="kw"><a href="https://rdrr.io/r/base/library.html" class="external-link">library</a></span><span class="op">(</span><span class="va"><a href="https://shiny.posit.co/" class="external-link">shiny</a></span><span class="op">)</span></span></span>
<span class="r-in"><span><span class="kw"><a href="https://rdrr.io/r/base/library.html" class="external-link">library</a></span><span class="op">(</span><span class="va"><a href="https://rstudio.github.io/bslib/" class="external-link">bslib</a></span><span class="op">)</span></span></span>
<span class="r-in"><span><span class="kw"><a href="https://rdrr.io/r/base/library.html" class="external-link">library</a></span><span class="op">(</span><span class="va"><a href="https://mirai.r-lib.org" class="external-link">mirai</a></span><span class="op">)</span></span></span>
<span class="r-in"><span></span></span>
<span class="r-in"><span><span class="co"># Set background processes for running tasks</span></span></span>
<span class="r-in"><span><span class="fu"><a href="https://mirai.r-lib.org/reference/daemons.html" class="external-link">daemons</a></span><span class="op">(</span><span class="fl">1</span><span class="op">)</span></span></span>
<span class="r-in"><span><span class="co"># Reset when the app is stopped</span></span></span>
<span class="r-in"><span><span class="fu"><a href="onStop.html">onStop</a></span><span class="op">(</span><span class="kw">function</span><span class="op">(</span><span class="op">)</span> <span class="fu"><a href="https://mirai.r-lib.org/reference/daemons.html" class="external-link">daemons</a></span><span class="op">(</span><span class="fl">0</span><span class="op">)</span><span class="op">)</span></span></span>
<span class="r-in"><span></span></span>
<span class="r-in"><span><span class="va">ui</span> <span class="op">&lt;-</span> <span class="fu"><a href="https://rstudio.github.io/bslib/reference/page.html" class="external-link">page_fluid</a></span><span class="op">(</span></span></span>
<span class="r-in"><span> <span class="fu"><a href="titlePanel.html">titlePanel</a></span><span class="op">(</span><span class="st">"Extended Task Demo"</span><span class="op">)</span>,</span></span>
<span class="r-in"><span> <span class="fu"><a href="https://rstudio.github.io/htmltools/reference/builder.html" class="external-link">p</a></span><span class="op">(</span></span></span>
<span class="r-in"><span> <span class="st">'Click the button below to perform a "calculation"'</span>,</span></span>
<span class="r-in"><span> <span class="st">"that takes a while to perform."</span></span></span>
<span class="r-in"><span> <span class="op">)</span>,</span></span>
<span class="r-in"><span> <span class="fu"><a href="https://rstudio.github.io/bslib/reference/input_task_button.html" class="external-link">input_task_button</a></span><span class="op">(</span><span class="st">"recalculate"</span>, <span class="st">"Recalculate"</span><span class="op">)</span>,</span></span>
<span class="r-in"><span> <span class="fu"><a href="https://rstudio.github.io/htmltools/reference/builder.html" class="external-link">p</a></span><span class="op">(</span><span class="fu"><a href="textOutput.html">textOutput</a></span><span class="op">(</span><span class="st">"result"</span><span class="op">)</span><span class="op">)</span></span></span>
<span class="r-in"><span><span class="op">)</span></span></span>
<span class="r-in"><span></span></span>
<span class="r-in"><span><span class="va">server</span> <span class="op">&lt;-</span> <span class="kw">function</span><span class="op">(</span><span class="va">input</span>, <span class="va">output</span><span class="op">)</span> <span class="op">{</span></span></span>
<span class="r-in"><span> <span class="va">rand_task</span> <span class="op">&lt;-</span> <span class="va">ExtendedTask</span><span class="op">$</span><span class="fu">new</span><span class="op">(</span><span class="kw">function</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span></span></span>
<span class="r-in"><span> <span class="fu"><a href="https://mirai.r-lib.org/reference/mirai.html" class="external-link">mirai</a></span><span class="op">(</span></span></span>
<span class="r-in"><span> <span class="op">{</span></span></span>
<span class="r-in"><span> <span class="co"># Slow operation goes here</span></span></span>
<span class="r-in"><span> <span class="fu"><a href="https://rdrr.io/r/base/Sys.sleep.html" class="external-link">Sys.sleep</a></span><span class="op">(</span><span class="fl">2</span><span class="op">)</span></span></span>
<span class="r-in"><span> <span class="fu"><a href="https://rdrr.io/r/base/sample.html" class="external-link">sample</a></span><span class="op">(</span><span class="fl">1</span><span class="op">:</span><span class="fl">100</span>, <span class="fl">1</span><span class="op">)</span></span></span>
<span class="r-in"><span> <span class="op">}</span></span></span>
<span class="r-in"><span> <span class="op">)</span></span></span>
<span class="r-in"><span> <span class="op">}</span><span class="op">)</span></span></span>
<span class="r-in"><span></span></span>
<span class="r-in"><span> <span class="co"># Make button state reflect task.</span></span></span>
<span class="r-in"><span> <span class="co"># If using R &gt;=4.1, you can do this instead:</span></span></span>
<span class="r-in"><span> <span class="co"># rand_task &lt;- ExtendedTask$new(...) |&gt; bind_task_button("recalculate")</span></span></span>
<span class="r-in"><span> <span class="fu"><a href="https://rstudio.github.io/bslib/reference/bind_task_button.html" class="external-link">bind_task_button</a></span><span class="op">(</span><span class="va">rand_task</span>, <span class="st">"recalculate"</span><span class="op">)</span></span></span>
<span class="r-in"><span></span></span>
<span class="r-in"><span> <span class="fu"><a href="observeEvent.html">observeEvent</a></span><span class="op">(</span><span class="va">input</span><span class="op">$</span><span class="va">recalculate</span>, <span class="op">{</span></span></span>
<span class="r-in"><span> <span class="co"># Invoke the extended in an observer</span></span></span>
<span class="r-in"><span> <span class="va">rand_task</span><span class="op">$</span><span class="fu">invoke</span><span class="op">(</span><span class="op">)</span></span></span>
<span class="r-in"><span> <span class="op">}</span><span class="op">)</span></span></span>
<span class="r-in"><span></span></span>
<span class="r-in"><span> <span class="va">output</span><span class="op">$</span><span class="va">result</span> <span class="op">&lt;-</span> <span class="fu"><a href="renderPrint.html">renderText</a></span><span class="op">(</span><span class="op">{</span></span></span>
<span class="r-in"><span> <span class="co"># React to updated results when the task completes</span></span></span>
<span class="r-in"><span> <span class="va">number</span> <span class="op">&lt;-</span> <span class="va">rand_task</span><span class="op">$</span><span class="fu">result</span><span class="op">(</span><span class="op">)</span></span></span>
<span class="r-in"><span> <span class="fu"><a href="https://rdrr.io/r/base/paste.html" class="external-link">paste0</a></span><span class="op">(</span><span class="st">"Your number is "</span>, <span class="va">number</span>, <span class="st">"."</span><span class="op">)</span></span></span>
<span class="r-in"><span> <span class="op">}</span><span class="op">)</span></span></span>
<span class="r-in"><span><span class="op">}</span></span></span>
<span class="r-in"><span></span></span>
<span class="r-in"><span><span class="fu"><a href="shinyApp.html">shinyApp</a></span><span class="op">(</span><span class="va">ui</span>, <span class="va">server</span><span class="op">)</span></span></span>
<span class="r-in"><span><span class="op">}</span></span></span>
</code></pre></div>
</div>
</div>
<div class="col-md-3 hidden-xs hidden-sm" id="pkgdown-sidebar">
<nav id="toc" data-toggle="toc" class="sticky-top"><h2 data-toc-skip>Contents</h2>
</nav></div>
</div>
<footer><div class="copyright">
<p></p><p>Developed by Winston Chang, Joe Cheng, JJ Allaire, Carson Sievert, Barret Schloerke, Garrick Aden-Buie, Yihui Xie, Jeff Allen, Jonathan McPherson, Alan Dipert, Barbara Borges, Posit Software, PBC.</p>
</div>
<div class="pkgdown">
<p></p><p>Site built with <a href="https://pkgdown.r-lib.org/" class="external-link">pkgdown</a> 2.2.0.</p>
</div>
</footer></div>
</body></html>