run a background task from a http request

  • Last Update :
  • Techknowledgy :

This is useful for operations that need to happen after a request, but that the client doesn't really have to be waiting for the operation to complete before receiving the response.,You can define background tasks to be run after returning a response.,FastAPI knows what to do in each case and how to re-use the same object, so that all the background tasks are merged together and are run in the background afterwards:,Import and use BackgroundTasks with parameters in path operation functions and dependencies to add background tasks.

from fastapi
import BackgroundTasks, FastAPI

app = FastAPI()

def write_notification(email: str, message = ""):
   with open("log.txt", mode = "w") as email_file:
   content = f "notification for {email}: {message}"
email_file.write(content)

@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
   background_tasks.add_task(write_notification, email, message = "some notification")
return {
   "message": "Notification sent in the background"
}
from typing
import Union

from fastapi
import BackgroundTasks, Depends, FastAPI

app = FastAPI()

def write_log(message: str):
   with open("log.txt", mode = "a") as log:
   log.write(message)

def get_query(background_tasks: BackgroundTasks, q: Union[str, None] = None):
   if q:
   message = f "found query: {q}\n"
background_tasks.add_task(write_log, message)
return q

@app.post("/send-notification/{email}")
async def send_notification(
      email: str, background_tasks: BackgroundTasks, q: str = Depends(get_query)
   ):
   message = f "message to {email}\n"
background_tasks.add_task(write_log, message)
return {
   "message": "Message sent"
}
from fastapi
import BackgroundTasks, Depends, FastAPI

app = FastAPI()

def write_log(message: str):
   with open("log.txt", mode = "a") as log:
   log.write(message)

def get_query(background_tasks: BackgroundTasks, q: str | None = None):
   if q:
   message = f "found query: {q}\n"
background_tasks.add_task(write_log, message)
return q

@app.post("/send-notification/{email}")
async def send_notification(
      email: str, background_tasks: BackgroundTasks, q: str = Depends(get_query)
   ):
   message = f "message to {email}\n"
background_tasks.add_task(write_log, message)
return {
   "message": "Message sent"
}

Suggestion : 2

Last modified: Aug 8, 2022, by MDN contributors

<p>
   Demonstration of using cooperatively scheduled background tasks using the
   <code>requestIdleCallback()</code> method.
</p>

<div id="container">
   <div class="label">Decoding quantum filament tachyon emissions…</div>

   <progress id="progress" value="0"></progress>

   <button class="button" id="startButton">Start</button>

   <div class="label counter">
      Task <span id="currentTaskNumber">0</span> of <span id="totalTaskCount">0</span>
   </div>
</div>

<div id="logBox">
   <div class="logHeader">Log</div>
   <div id="log"></div>
</div>
body {
   font - family: "Open Sans", "Lucida Grande", "Arial", sans - serif;
   font - size: 16 px;
}

#logBox {
   margin - top: 16 px;
   width: 400 px;
   height: 500 px;
   border - radius: 6 px;
   border: 1 px solid black;
   box - shadow: 4 px 4 px 2 px black;
}

.logHeader {
   margin: 0;
   padding: 0 6 px 4 px;
   height: 22 px;
   background - color: lightblue;
   border - bottom: 1 px solid black;
   border - radius: 6 px 6 px 0 0;
}

#log {
   font: 12 px "Courier",
   monospace;
   padding: 6 px;
   overflow: auto;
   overflow - y: scroll;
   width: 388 px;
   height: 460 px;
}

#container {
   width: 400 px;
   padding: 6 px;
   border - radius: 6 px;
   border: 1 px solid black;
   box - shadow: 4 px 4 px 2 px black;
   display: block;
   overflow: auto;
}

.label {
   display: inline - block;
}

.counter {
   text - align: right;
   padding - top: 4 px;
   float: right;
}

.button {
   padding - top: 2 px;
   padding - bottom: 4 px;
   width: 100 px;
   display: inline - block;
   float: left;
   border: 1 px solid black;
   cursor: pointer;
   text - align: center;
   margin - top: 0;
   color: white;
   background - color: darkgreen;
}

#progress {
   width: 100 % ;
   padding - top: 6 px;
}
const taskList = [];
let totalTaskCount = 0;
let currentTaskNumber = 0;
let taskHandle = null;
const totalTaskCountElem = document.getElementById("totalTaskCount");
const currentTaskNumberElem = document.getElementById("currentTaskNumber");
const progressBarElem = document.getElementById("progress");
const startButtonElem = document.getElementById("startButton");
const logElem = document.getElementById("log");
let logFragment = null;
let statusRefreshScheduled = false;
requestIdleCallback = requestIdleCallback || ((handler) => {
   const startTime = Date.now();

   return setTimeout(() => {
      handler({
         didTimeout: false,
         timeRemaining() {
            return Math.max(0, 50.0 - (Date.now() - startTime));
         }
      });
   }, 1);
});

cancelIdleCallback = cancelIdleCallback || ((id) => {
   clearTimeout(id);
});

Suggestion : 3

Currently, I use the following approach

Threads.@spawn begin
HTTP.request("POST", url, header, event)
end

Another solution I found is the package WorkerUtilities where you could call it like this, I guess:

worker = WorkerUtilities.@spawn begin
HTTP.request("POST", url, header, event)
end
response = fetch(worker)

Suggestion : 4

04/13/2022

The way you add one or multiple IHostedServices into your WebHost or Host is by registering them up through the AddHostedService extension method in an ASP.NET Core WebHost (or in a Host in .NET Core 2.1 and above). Basically, you have to register the hosted services within the familiar ConfigureServices() method of the Startup class, as in the following code from a typical ASP.NET WebHost.

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    //Other DI registrations;

    // Register Hosted Services
    services.AddHostedService<GracePeriodManagerService>();
    services.AddHostedService<MyHostedServiceB>();
    services.AddHostedService<MyHostedServiceC>();
    //...
}

The next code is the abstract BackgroundService base class as implemented in .NET.

// Copyright (c) .NET Foundation. Licensed under the Apache License, Version 2.0.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
    private Task _executingTask;
    private readonly CancellationTokenSource _stoppingCts =
                                                   new CancellationTokenSource();

    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        // Store the task we're executing
        _executingTask = ExecuteAsync(_stoppingCts.Token);

        // If the task is completed then return it,
        // this will bubble cancellation and failure to the caller
        if (_executingTask.IsCompleted)
        {
            return _executingTask;
        }

        // Otherwise it's running
        return Task.CompletedTask;
    }

    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
        // Stop called without start
        if (_executingTask == null)
        {
            return;
        }

        try
        {
            // Signal cancellation to the executing method
            _stoppingCts.Cancel();
        }
        finally
        {
            // Wait until the task completes or the stop token triggers
            await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
                                                          cancellationToken));
        }

    }

    public virtual void Dispose()
    {
        _stoppingCts.Cancel();
    }
}

When deriving from the previous abstract base class, thanks to that inherited implementation, you just need to implement the ExecuteAsync() method in your own custom hosted service class, as in the following simplified code from eShopOnContainers which is polling a database and publishing integration events into the Event Bus when needed.

public class GracePeriodManagerService : BackgroundService
{
    private readonly ILogger<GracePeriodManagerService> _logger;
    private readonly OrderingBackgroundSettings _settings;

    private readonly IEventBus _eventBus;

    public GracePeriodManagerService(IOptions<OrderingBackgroundSettings> settings,
                                     IEventBus eventBus,
                                     ILogger<GracePeriodManagerService> logger)
    {
        // Constructor's parameters validations...
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogDebug($"GracePeriodManagerService is starting.");

        stoppingToken.Register(() =>
            _logger.LogDebug($" GracePeriod background task is stopping."));

        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogDebug($"GracePeriod task doing background work.");

            // This eShopOnContainers method is querying a database table
            // and publishing events into the Event Bus (RabbitMQ / ServiceBus)
            CheckConfirmedGracePeriodOrders();

            await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
        }

        _logger.LogDebug($"GracePeriod background task is stopping.");
    }

    .../...
}

Suggestion : 5

The other way we can get data from the Background Task is by using anvil.server.task_state. This is a special object that allows the Background Task function to store data about itself. By default, it’s a dictionary, so you can assign things to its keys. Let’s store the total number of URLs we’re crawling:,Tasks are launched by calling anvil.server.launch_background_task from a server function. This works just like anvil.server.call - the first argument is the function name, and all other arguments are passed to the function. It returns a Task object, which the app can use to access data from the Background Task.,This returns a list of Task objects. The Task object has a method to get the name of the task - this is usually the name of the relevant server function. Modify that line to filter out crawl tasks in case other task types get added:,Having the task running in the background is great, but we need to get some information out of it. First, we’ll search the pages it’s storing. Then, we’ll display the crawling process in the UI in real time.

@anvil.server.background_task
def crawl(sitemap_url):
   print('Crawling: ' + sitemap_url)
@anvil.server.callable
def launch_one_crawler(sitemap_url):
   ""
"Launch a single crawler background task."
""
task = anvil.server.launch_background_task('crawl', sitemap_url)
return task
  def button_run_click(self, ** event_args):
     ""
  "This method is called when the button is clicked"
  ""
  self.task = anvil.server.call('launch_one_crawler', self.text_box_sitemap.text)
@anvil.server.background_task
def crawl(sitemap_url):
# ... the code we wrote before, then ...

# Get the contents of the sitemap
response = anvil.http.request(sitemap_url)
sitemap = response.get_bytes()

# Parse out the URLs
urls = []
for line in sitemap.split('\n'):
if '<loc>' in line:
   urls.append(line.split('<loc>')[1].split('</loc>')[0])
def get_pages(urls):
   for n, url in enumerate(urls):
   url = url.rstrip('/')

# Get the page
try:
print("Requesting URL " + url)
response = anvil.http.request(url)
except:
   # If the fetch failed, just
try the other URLs
continue
from datetime
import datetime

def store_page(url, html):
   # Find the Data Tables row, or add one
with anvil.tables.Transaction() as txn:
   data = app_tables.pages.get(url = url) or app_tables.pages.add_row(url = url)

# Update the data in the Data Tables
data['html'] = html
data['last_indexed'] = datetime.now()