Почему «ожидание» нельзя использовать в обычных функциях?

Исправление: как следует из двух ответов, проблема выглядит специфичной для dart, поскольку C# реализует Task.Wait, позволяющий достичь желаемого эффекта. Я был бы признателен за любую дополнительную информацию о причинах, по которым это невозможно в дартс или как этого добиться там. Я был бы рад закрыть вопрос в его нынешнем виде.


Бывают ситуации, когда имеет смысл использовать синхронные функции для внутреннего использования асинхронных вызовов, ожидающих их завершения перед доставкой результата. Это явно запрещено как в C#, так и в Dart: все, что использует await, должно быть помечено как async (таким образом возвращается будущее).

Простой пример в Dart, но то же самое верно и для C#:

Отказ от ответственности: я бы так не поступил, поэтому не предлагайте "правильное" решение преобразования всех методов в асинхронные; код здесь просто для иллюстрации вопроса.

У меня есть полностью асинхронная реализация универсальной оболочки для HTTP-запросов к определенному REST API. Он выполняет вход, аутентификацию и т. д.:

class RequestProcessor {
     Future post(String path, [payload]) async {
          return request("POST", path, payload);
     }
}

Я хотел бы реализовать высокоуровневый интерфейс API для пользователей, переводящий запросы в REST-API. Некоторые из этих методов я хотел бы иметь синхронными. Это то, что я хотел бы и не могу иметь:

class API {
    final RequestProcessor processor;

    API(this.processor);

    // this takes long and should be asynchronous, happy with a future (OK)
    Future getQueryResults(query) async {
        return await processor.post("/query", query);
    }

    // here I would prefer to have it synchronous (COMPILE ERROR)
    String getVersion() {
        return await processor.post("/version");
    }
}

Вопрос. Почему это запрещено?


person Oleg Sklyar    schedule 26.10.2015    source источник
comment
спросите разработчиков языка, а не нас.   -  person Daniel A. White    schedule 26.10.2015
comment
comment
Это именно то, что делает ключевое слово async: включает ключевое слово await. Возможно, они могли бы обойтись и без async, но это вызвало бы двусмысленность. В этой статье объясняются причины этого: блоги .msdn.com/b/ericlippert/archive/2010/11/11/   -  person Dennis_E    schedule 26.10.2015
comment
В Dart асинхронность является асинхронной и остается асинхронной. С этим ничего нельзя поделать.   -  person Günter Zöchbauer    schedule 26.10.2015
comment
Хотелось бы понять дизайнерское решение, стоящее за этим: почему так? Очевидно, C # может справиться с этим.   -  person Oleg Sklyar    schedule 26.10.2015
comment
@Oleg: C# не может с этим справиться. Есть несколько разных хаков, чтобы попытаться заставить его, но это крайне неестественно, и нет ни одного хака, который работал бы во всех сценариях. В частности, Wait() является пережитком библиотеки параллельной обработки и не должен использоваться в асинхронных задачах. Суть в том, что будущее представляет собой будущий результат. Вы не можете вернуть результат будущего сейчас (т. е. синхронно).   -  person Stephen Cleary    schedule 26.10.2015
comment
@StephenCleary Но вы можете вернуть будущий результат, как только он будет доступен, и вы можете подождать, пока он не будет доступен. Что мне не хватает? Только что увидел поправку: так бы работало, но не должно использоваться. Если да, то почему?   -  person Oleg Sklyar    schedule 26.10.2015
comment
@Oleg: В C# Wait() является распространенной причиной взаимоблокировки. Всегда есть лучшее решение. В 99% случаев лучшее решение — сделать вызывающий код асинхронным.   -  person Stephen Cleary    schedule 26.10.2015


Ответы (4)


await — это асинхронное ожидание, поэтому нет смысла использовать его в синхронной функции. Чтобы быть асинхронным, метод должен возвращать значение при инициировании ожидаемой операции, тогда как синхронный метод должен выполняться до завершения.

Вместо этого вы можете использовать синхронное ожидание, и в этом случае ваш метод не обязательно должен быть async. В C# это можно сделать, вызвав метод Wait() для возвращаемой задачи.

person NeddySpaghetti    schedule 26.10.2015
comment
в c# вы можете вызвать Wait(), дождитесь завершения задачи, возможно, в dart есть что-то подобное. - person NeddySpaghetti; 26.10.2015
comment
Не похоже, чтобы в dart был подобный механизм, но я понял, что не знал об этой конкретной специфике C#. Спасибо. - person Oleg Sklyar; 26.10.2015

Это выбор дизайна. Dart является однопоточным — каждый синхронный вызов функции будет выполняться до завершения, прежде чем разрешить выполнение любого другого кода. Если вы разрешите синхронной функции блокироваться до тех пор, пока не завершится будущее, то никакой другой код не будет выполняться, и будущее никогда не завершится (поскольку код, который должен завершить его, не может быть запущен).

Возможна система, позволяющая выполнять другой код, в то время как одна синхронная функция заблокирована. Это требует либо наличия более одного активного стека (параллельного или нет), либо неявного преобразования всей программы в асинхронную. Ни один из вариантов не подходит при компиляции в JavaScript, поэтому Dart не пытался это сделать.

person lrn    schedule 26.10.2015
comment
История с блокировкой понятна: синхронные вызовы блокируются. Итак, смысл в том, чтобы быть однопоточным, и это то, что отличает его от C#, который обеспечивает синхронный Wait? - person Oleg Sklyar; 26.10.2015
comment
Да, C# уже многопоточный, так что заблокировать один поток не проблема. Dart является однопоточным (по необходимости, когда он компилируется в JavaScript, который также является однопоточным), поэтому он не может заблокировать один поток, не заблокировав при этом весь изолят. - person lrn; 28.10.2015

Потому что ожидание и возвращение будущего — это принципиально разные вещи.

Возврат будущего освобождает текущий поток и, возможно, планирует продолжение на более позднее время, ожидание блокирует текущий поток.

Например, в потоке пользовательского интерфейса возвращение будущего допустимо, но ожидание приведет к зависанию пользовательского интерфейса, в потоке веб-сервера ожидание заблокирует поток и не позволит веб-серверу обрабатывать дополнительные запросы и т. д.

Возможно даже, что ожидание того, что должно быть асинхронной операцией, вызовет взаимоблокировку.

.net обеспечивает Task.Wait, когда вы знаете, что ожидание в порядке, я не знаю о dart, но это не происходит автоматически (за исключением await внутри предложений catch в асинхронных методах в C #)

person Nir    schedule 26.10.2015
comment
Я очень хорошо понимаю причины ожидания, так что это не мой вопрос. В общем, все сводится к тому, почему у dart нет аналога C# Wait() или как этого можно добиться... - person Oleg Sklyar; 26.10.2015

В Dart пометка функции/метода async сообщает виртуальной машине, что ей необходимо переписать код перед его выполнением. await не меняет асинхронную функцию на синхронную, функция по-прежнему асинхронная, просто она допускает более приятный синтаксис, чем цепочки then(() { return ...then(() { return...})}).

Если вы сделаете асинхронный вызов, все, что последует, будет асинхронным, вы ничего не сможете с этим поделать.

person Günter Zöchbauer    schedule 26.10.2015
comment
Его вопрос заключался в том, почему эквивалент не работает в C #, я думаю. - person Dan; 26.10.2015
comment
Потому что асинхронные и неасинхронные методы имеют существенно разную семантику. Асинхронные методы преобразуются в конечный автомат, что позволяет «возобновлять» код (не лучшее слово для этого) после завершения асинхронного метода. - person Dan; 26.10.2015