JSAsyncScope structure
Helps preventing Node.JS process from exiting while we execute C# async functions.
public struct JSAsyncScope : IDisposable
Public Members
name | description |
---|---|
JSAsyncScope() | The default constructor. |
IsDisposed { get; } | |
Dispose() |
Remarks
Async function uses await
keyword that splits it up into several sequentially executed tasks. Each task can run in its own task scheduler. For running tasks in Node.JS main loop we use JSSynchronizationContext
. In the following code we start and end async function execution in JSSynchornizationContext if we start running it in the main loop thread:
public async void RunAsyncTask(JSDeferred deferred, int id)
{
// Capture current SynchornizationContext and run work in a background thread.
var data = await RetriveDataById(id);
// After exiting await we use captured SynchornizationContext to run remaining code in that context.
deferred.Resolve((JSValue)data.FullName);
}
The work in the background thread may take some time and Node.JS process can finish because it completed all current tasks. It is not aware about us running important code in the background thread. We must say to Node.JS that we plan to do some important work in its main loop after we finish the background task. The OpenAsyncScope
and CloseAsyncScope
can be used to do that:
public async void RunAsyncTask(JSDeferred deferred, int id)
{
// Ask Node.JS to keep process alive because we need its main loop.
JSSynchronizationContext.Current.OpenAsyncScope();
var data = await RetriveDataById(id);
deferred.Resolve((JSValue)data.FullName);
// Tell Node.JS that we finished using its main loop and Node.JS process can exit
// after completing current callback.
JSSynchronizationContext.Current.CloseAsyncScope();
}
Note that these two functions must be called in the main loop thread. The JSAsyncScope
is a convenience struct that calls these two functions for us automatically in it constructor and Dispose
method. We can rewrite the code above as:
public async void RunAsyncTask(JSDeferred deferred, int id)
{
// We must use 'using' keyword to call 'Dispose' in the end.
using var asyncScope = new JSAsyncScope();
var data = await RetriveDataById(id);
deferred.Resolve((JSValue)data.FullName);
}