Browse Source

Add examples & tidy commands section

+ Modified the headings to fit the formal aspect of docs.
+ Added more details regarding RunMode
pull/988/head
Hsu Still 7 years ago
parent
commit
1821ea15bb
3 changed files with 65 additions and 21 deletions
  1. +49
    -21
      docs/faq/Commands.md
  2. +7
    -0
      docs/faq/samples/commands/runmode-cmdattrib.cs
  3. +9
    -0
      docs/faq/samples/commands/runmode-cmdconfig.cs

+ 49
- 21
docs/faq/Commands.md View File

@@ -18,7 +18,7 @@ custom preconditions.
[RequireUserPermission]: xref:Discord.Commands.RequireUserPermissionAttribute
[Preconditions Addons]: https://github.com/Joe4evr/Discord.Addons/tree/master/src/Discord.Addons.Preconditions

## I'm getting an error about `Assembly#GetEntryAssembly`. What now?
## I'm getting an error about `Assembly#GetEntryAssembly`.

You may be confusing [CommandService#AddModulesAsync] with
[CommandService#AddModuleAsync]. The former is used to add modules
@@ -64,37 +64,64 @@ A brief example of service and dependency injection can be seen below.

By default, all commands are executed on the same thread as the
gateway task, which is responsible for keeping the connection from
your client to Discord alive. When you execute a long-running task,
your client to Discord alive. By default, when you execute a command,
this blocks the gateway from communicating for as long as the command
task is being executed. The library will warn you about any long
running event handler (in this case, the command handler) that
persists for more than 3 seconds.
persists for **more than 3 seconds**.

To resolve this, the library has designed a flag called [RunMode].
There are 2 main `RunMode`s. One being `RunMode.Sync`, which is the
default; another being `RunMode.Async`. `RunMode.Async` essentially
calls an unawaited Task and continues with the execution without
waiting for the command task to finish. You should use
`RunMode.Async` in either the [CommandAttribute] or the
[DefaultRunMode] flag in `CommandServiceConfig`.
Further details regarding `RunMode.Async` can be found below.

There are 2 main `RunMode`s.
1. `RunMode.Sync` (default)
2. `RunMode.Async`

You can set the `RunMode` either by specifying it individually via
the `CommandAttribute`, or by setting the global default with
the [DefaultRunMode] flag under `CommandServiceConfig`.

# [CommandAttribute](#tab/cmdattrib)

[!code-csharp[Command Attribute](samples/commands/runmode-cmdattrib.cs)]

# [CommandServiceConfig](#tab/cmdconfig)

[!code-csharp[Command Service Config](samples/commands/runmode-cmdconfig.cs)]

***

***

> [!IMPORTANT]
> While specifying `RunMode.Async` allows the command to be spun off
> to a different thread instead of the gateway thread,
> keep in mind that there will be **potential consequences**
> by doing so. Before applying this flag, please
> consider whether it is necessary to do so.
>
> Further details regarding `RunMode.Async` can be found below.

[RunMode]: xref:Discord.Commands.RunMode
[CommandAttribute]: xref:Discord.Commands.CommandAttribute
[DefaultRunMode]: xref:Discord.Commands.CommandServiceConfig#Discord_Commands_CommandServiceConfig_DefaultRunMode

## Okay, that's great and all, but how does `RunMode.Async` work, and if it's so great, why is the lib *not* using it by default?
## How does `RunMode.Async` work, and why is Discord.NET *not* using it by default?

`RunMode.Async` works by spawning a new `Task` with an unawaited
[Task.Run], essentially making `ExecuteAsyncInternalAsync`, the task
that is used to invoke the command task, to be finished on a
different thread. This means that [ExecuteAsync] will be forced to
return a successful [ExecuteResult] regardless of the execution.

As with any async operation, `RunMode.Async` also comes at a cost.
The following are the caveats with RunMode.Async,
1) You introduce race condition.
2) Unnecessary overhead caused by [async state machine].
3) [ExecuteAsync] will immediately return [ExecuteResult] instead of
other result types (this is particularly important for those who wish
to utilize [RuntimeResult] in 2.0).
4) Exceptions are swallowed.
The following are the known caveats with `RunMode.Async`,
1. You introduce race condition.
2. Unnecessary overhead caused by [async state machine].
3. [ExecuteAsync] will immediately return [ExecuteResult] instead of
other result types (this is particularly important for those who wish
to utilize [RuntimeResult] in 2.0).
4. Exceptions are swallowed.

However, there are ways to remedy #3 and #4.
However, there are ways to remedy some of these.

For #3, in Discord.NET 2.0, the library introduces a new event called
[CommandExecuted], which is raised whenever the command is
@@ -104,7 +131,8 @@ the `RunMode` type and will return the appropriate execution result.
For #4, exceptions are caught in [CommandService#Log] event under
[LogMessage.Exception] as [CommandException].

[async state machine]: https://www.red-gate.com/simple-talk/dotnet/net-tools/c-async-what-is-it-and-how-does-it-work/))
[Task.Run]: https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.run
[async state machine]: https://www.red-gate.com/simple-talk/dotnet/net-tools/c-async-what-is-it-and-how-does-it-work/
[ExecuteAsync]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_ExecuteAsync_Discord_Commands_ICommandContext_System_Int32_System_IServiceProvider_Discord_Commands_MultiMatchHandling_
[ExecuteResult]: xref:Discord.Commands.ExecuteResult
[RuntimeResult]: xref:Discord.Commands.RuntimeResult


+ 7
- 0
docs/faq/samples/commands/runmode-cmdattrib.cs View File

@@ -0,0 +1,7 @@
[Command("process", RunMode = RunMode.Async)]
public async Task ProcessAsync(string input)
{
// Does heavy calculation here.
await Task.Delay(TimeSpan.FromMinute(1));
await ReplyAsync(input);
}

+ 9
- 0
docs/faq/samples/commands/runmode-cmdconfig.cs View File

@@ -0,0 +1,9 @@
public class Setup
{
private readonly CommandService _command;
public Setup()
{
var config = new CommandServiceConfig{DefaultRunMode = RunMode.Async};
_command = new CommandService(config);
}
}

Loading…
Cancel
Save