Que tal não ter uma palavra-chave?
Gostaria que o compilador percebesse que, na maioria das vezes, quando chamo um método assíncrono, quero o resultado.
Document doc = DownloadDocumentAsync();
É isso aí. A razão pela qual as pessoas estão tendo dificuldade em pensar em uma palavra-chave para isso é porque é como ter uma palavra-chave para "faça o que você faria se as coisas fossem perfeitamente normais". Esse deve ser o padrão, não requer uma palavra-chave.
Atualizar
Originalmente, sugeri que o compilador deveria ficar esperto com a inferência de tipo para descobrir o que fazer. Pensando mais sobre isso, eu manteria a implementação existente no CTP como ela é, mas faria algumas adições triviais, para reduzir os casos em que você precisaria usar a await
palavra - chave explicitamente.
Nós inventamos um atributo: [AutoAwait]
. Isso só pode ser aplicado a métodos. Uma maneira de aplicar isso ao seu método é marcá-lo async
. Mas você também pode fazer isso manualmente, por exemplo:
[AutoAwait]
public Task<Document> DownloadDocumentAsync()
Então, dentro de qualquer async
método, o compilador assumirá que você deseja aguardar uma chamada DownloadDocumentAsync
, para que você não precise especificá-lo. Qualquer chamada para esse método o aguardará automaticamente.
Document doc = DownloadDocumentAsync();
Agora, se você deseja "ficar esperto" e obter o Task<Document>
, use um operador start
, que só pode aparecer antes de uma chamada de método:
Task<Document> task = start DownloadDocumentAsync();
Legal, eu acho. Agora, uma chamada simples de método significa o que normalmente significa: aguarde a conclusão do método. E start
indica algo diferente: não espere.
Para o código que aparece fora de um async
método, a única maneira de você chamar um [AutoAwait]
método é prefixando-o start
. Isso obriga a escrever um código com o mesmo significado, independentemente de ele aparecer async
ou não em um método.
Então eu começo a ficar ganancioso! :)
Em primeiro lugar, quero async
aplicar aos métodos de interface:
interface IThing
{
async int GetCount();
}
Basicamente, significa que o método de implementação deve retornar Task<int>
ou algo compatível await
e os chamadores do método terão [AutoAwait]
comportamento.
Além disso, quando eu implemento o método acima, desejo poder escrever:
async int GetCount()
Portanto, não tenho que mencionar Task<int>
como o tipo de retorno.
Além disso, quero async
aplicar a tipos de delegação (que, afinal, são como interfaces com um método). Então:
public async delegate TResult AsyncFunc<TResult>();
Um async
delegado tem - você adivinhou - [AutoAwait]
comportamento. A partir de um async
método, você pode chamá-lo e ele será await
editado automaticamente (a menos que você escolha apenas start
). E então, se você disser:
AsyncFunc<Document> getDoc = DownloadDocumentAsync;
Isso simplesmente funciona. Não é uma chamada de método. Nenhuma tarefa foi iniciada ainda - uma async delegate
não é uma tarefa. É uma fábrica para fazer tarefas. Você pode dizer:
Document doc = getDoc();
E isso iniciará uma tarefa, esperará que ela termine e dê o resultado. Ou você pode dizer:
Task<Document> t = start getDoc();
Portanto, um lugar onde o "encanamento" vaza é que, se você deseja fazer um delegado para um async
método, precisa saber usar um async delegate
tipo. Então, em vez de Func
você deve dizer AsyncFunc
, e assim por diante. Embora um dia esse tipo de coisa possa ser corrigido por inferência de tipo aprimorada.
Outra pergunta é o que deveria acontecer se você disser iniciar em um método comum (não assíncrono). Obviamente, um erro de compilação seria a opção segura. Mas existem outras possibilidades.