0

Я думал, что быть умным в поиске способа вызвать метод aync в CTOR:Почему исключение проглатывается в асинхронном действии в конструкторе?

public AppStateModel(IBranchClient branchClient) 
{ 
    _branchClient = branchClient; 
    var loadBranch = new Action(async() => 
    { 
     DataProviderReadResult<BranchDetailViewModel> result = await _branchClient.ReadOneItemAsync(AppSettings.BranchId, _initCts.Token); 
    }); 
    loadBranch(); 
} 

Но тело действия бросает исключение, которое я вхожу и повторно перекидной с простым throw;, но этот ctor выполняет отлично, а остальная часть моего кода продолжает работать, как будто ничего не происходит. Почему это?

+1

Потому что это 'async'? 'loadBranch()' возвращается в 'await', если' _branchClient.ReadOnItemAsync() 'еще не завершен. Таким образом, конструктор успешно возвращается. –

+2

Почему вы пытаетесь вызвать метод async в конструкторе? Конструкторы не должны выполнять никаких сложных операций. Почему бы не использовать статический метод вместо этого? Это может быть обычным асинхронным методом без какой-либо этой глупости :) – Luaan

+0

@ Luaan Это просто быстро и грязно, пока я не закончу свою текущую историю, тогда у меня есть еще одна задача на доске, чтобы покончить с глупостью. Мне не нужно знать, как заставить его работать, потому что я никогда не сделаю такого в конечном коде. Я просто спрашиваю, потому что мне любопытно. – ProfK

ответ

6

Поскольку действие является асинхронным. loadBranch() возвращается, как только будет достигнут первый await, и невозможно выполнить исключение, которое вы ожидаете - это часть информации в Task, которую вы проигнорировали (используя Action вместо Func<Task>).

В общем, вы только что написали более затемненный версию этого:

_branchClient.ReadOneItemAsync(AppSettings.BranchId, _initCts.Token); 

.NET Конструкторы по своей сути синхронными. Они не должны делать ничего, где вам будет полезно использовать асинхронный код, - и это хорошая идея сделать как можно меньше в конструкторе (или методах, вызываемых конструктором). Если вам нужны сложные действия, асинхронный код, ввод-вывод, много работы центрального процессора ... используйте статический метод. И так как вы используете асинхронный код, сделайте его возвратом Task<AppStateModel>, подходящим для всего асинхронного потока.

Также обратите внимание, что исключение будет не быть проглоченным при старших времени выполнения .NET. Предполагая, что контекст синхронизации не существует, исключение все равно бросается в фоновый поток (где отправляется продолжение на асинхронную операцию) - и по умолчанию для необработанных исключений в потоках потоков потоков используется «приведение всего приложения». Это произойдет, когда объект Task будет завершен, поэтому он будет отделен от всей вашей логики программы, в значительной степени случайной, насколько вы можете судить. В конце концов, что еще вы можете сделать - нет хорошего места, где исключение можно было бы наблюдать, и единственный корень там когда-либо был в основном сказал: «Меня не волнует, что происходит с этой задачей». Но с учетом того, насколько сложно было гарантировать, что каждое исключение должным образом соблюдается и обрабатывается, значение по умолчанию было изменено на «незаметные исключения игнорируются».

+1

Исключение никогда не будет проглочено здесь. Вы думаете о ненаблюдаемом поведении исключения задачи, но это 'async void' (нет' Task'). Основываясь на описании op, исключение просто не произошло * еще * - оно будет отображаться, если они позволят запустить все приложение. –

+0

Спасибо, @Luaan. Я никогда не планировал использовать это в «реальном» коде, это было скорее экспериментальным быстрым и грязным, чтобы загрузить ветку, чтобы я мог сосредоточиться на истории, в которой я был занят, которая нуждалась в ветке. Я просто хотел объяснить, почему произошло, и вы дали очень хороший. – ProfK

2

Давайте разберемся, что здесь происходит: у вас есть делегат Action, который указывает на анонимный метод async void. Что означает async void? Это означает, что метод фактически возвращает Task, который инкапсулирует асинхронную логику.

Что это значит, когда вы вызываете loadBranch, он выполняет асинхронный метод. Когда метод async попадает в вызов await, он возвращает объект Task, который позволяет подождать, добавить продолжение или что-то еще. Но поскольку у вас нет явной переменной Task для ее захвата, вы просто позволяете своему конструктору выйти из сферы действия, без какого-либо кода, обрабатывающего продолжение задачи. Это означает, что когда задача запускает, ctor уже вышел.

Смежные вопросы