Как работать с HttpTaskAsyncHandler
public class FooHandler : HttpTaskAsyncHandler
{
public override async Task ProcessRequestAsync(HttpContext context)
{
return await new AdRequest().ProcessRequest();
// getting error here. "Return type of async type is void"
}
}
public class FooRequest
{
public async Task<String> ProcessRequest()
{
//return await "foo"; obviously nothing to wait here
}
}
Я хочу сделать асинхронный обработчик и просто хочу вернуть строку. Как я могу заставить это работать? и есть ли краткое указание на работу с асинхронными методами и задачами?
3 ответов:
Несколько пунктов:
- Вы можете
awaitлюбыеTask, а не только те, которые возвращаются из методовasync.- Методы
asyncоборачивают возвращаемое значение вTask<TResult>; если возвращаемое значение отсутствует, они оборачивают само возвращение вTask.- есть несколько доступных методов удобства, например,
Task.FromResult, Если вам не нужны накладные расходы методаasync.- только сделайте метод
async, Если вам нужно использоватьawaitв нем. Если вам не нужно делать методasync, не надо.Мой асинхронный/ждут интро полезная.
public class FooHandler : HttpTaskAsyncHandler { public override Task ProcessRequestAsync(HttpContext context) { return new AdRequest().ProcessRequest(); } } public class AdRequest { public Task<String> ProcessRequest() { return Task.FromResult("foo"); } }
Вы не должны "возвращать" задачу, компилятор сделает это неявно, так как это асинхронная функция:
public override async Task ProcessRequestAsync(HttpContext context) { await new AdRequest().ProcessRequest(); } public async Task<String> ProcessRequest() { return "foo"; }Это другой способ, более близкий к тому, что вы пытались сделать: (без async / await)
public override Task ProcessRequestAsync(HttpContext context) { return new AdRequest().ProcessRequest(); } public Task<String> ProcessRequest() { return Task.Return("foo"); }Общая ссылка на
asyncявляется здесь По существу, добавление модификатораasyncк методу делает его возвращающимTaskнеявно. Если вы вернете int, он превратит его вTask<int>.awaitделает обратное, превращаяTask<int>вint.
Это действительно асинхронный метод:
public Task<string> ProcessRequest() { var textFile = File.OpenText("file.txt"); var readTask = textFile.ReadToEndAsync(); readTask.ContinueWith(previousTask => textFile.Dispose()); return readTask; }Если вы запускаете этот метод с большим файлом или файлом на медленном диске, выполнение вернется к вызывающему задолго до окончания чтения файла. В Примере Стивена Клири вызывающий получит обратно контроль только тогда, когда результат ("foo") будет закончен вычислением.
Dispose должен быть в ContinueWith, потому что выполнение метода вернется к вызывающему до завершения чтения файла, поэтому файл не может быть закрыт в ProcessRequest метод.
Можно, конечно, начать свою собственную задачу.
public Task<string> ProcessRequest(CancellationToken cancellationToken) { var readTask = Task.Run(() => { using (var textFile = File.OpenText("file.txt")) { var text = textFile.ReadToEnd(); cancellationToken.ThrowIfCancellationRequested(); var processedText = text.Replace("foo", "bar"); return processedText; } }); return readTask; }Хорошей практикой является иметь CancellationToken и периодически проверять, была ли запрошена отмена, чтобы разрешить отмену длительных операций.
Правка 1
As @Stephen Cleary выделил первый образец, и это приводит к приблизительно или, возможно, точно такому же CIL:
public async Task<string> ProcessRequest() { using (var textFile = File.OpenText("file.txt")) { var s = await textFile.ReadToEndAsync(); return s; } }В основном компилятор преобразует код, следующий за текстовым файлом await.ReadToEndAsync () в Продолжайте.
Каждый синтаксис имеет свои преимущества, я предпочитаю, чтобы 1-2 строки (т. е. dispose и log) шли в ContinueWith, более сложные продолжения используют await.
Comments