async & await - pesquisa de alternativas [closed]

14

Agora que sabemos o que está reservado para c # 5, aparentemente ainda há uma oportunidade para influenciarmos a escolha das duas novas palavras-chave para ' Asynchrony " que foram anunciados por Anders Heijsberg ontem no PDC10 .

async void ArchiveDocuments(List<Url> urls) {
    Task archive = null;
    for(int i = 0; i < urls.Count; ++i) {
        var document = await FetchAsync(urls[i]);
        if (archive != null)
            await archive;
        archive = ArchiveAsync(document);
    }
}

Eric Lippert tem uma explicação sobre a escolha das duas palavras-chave atuais e a maneira como elas foram mal interpretadas em estudos de usabilidade. Os comentários têm várias outras proposições.

Por favor - uma sugestão por resposta, as duplicatas serão eliminadas.

    
por Benjol 29.10.2010 / 22:35
fonte

13 respostas

6

Como não estou certo sobre o significado / necessidade de async , não posso argumentar com isso, mas minha melhor sugestão para substituir await é:

yield while (procure! sem novas palavras-chave)

Nota tendo pensado nisso um pouco mais, gostaria de saber se reutilizar while desta forma é uma boa ideia - a tendência natural seria esperar um booleano depois disso.

(Pensa: encontrar boas palavras-chave é como encontrar bons nomes de domínio:)

    
por 29.10.2010 / 22:41
fonte
5

Que tal não ter uma palavra-chave?

Eu gostaria que o compilador percebesse que, na maioria das vezes, quando eu chamo um método assíncrono, eu quero o resultado dele.

Document doc = DownloadDocumentAsync();

É isso. A razão pela qual as pessoas estão tendo dificuldade em pensar em uma palavra-chave para essa coisa é porque é como ter uma palavra-chave para "fazer o que você faria se as coisas fossem perfeitamente normais". Esse deve ser o padrão, não exigir uma palavra-chave.

Atualizar

Eu originalmente sugeri que o compilador deveria ficar esperto com a inferência de tipos para descobrir o que fazer. Pensando mais nisso, eu manteria a implementação existente no CTP como ela é, mas fizesse algumas adições triviais a ela, para reduzir os casos em que você precisaria usar a palavra-chave await explicitamente. / p>

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 em async . Mas você também pode fazer isso manualmente, por exemplo:

[AutoAwait]
public Task<Document> DownloadDocumentAsync()

Em seguida, dentro de qualquer método async , o compilador assumirá que você deseja aguardar em uma chamada para DownloadDocumentAsync , portanto, não é necessário especificá-lo. Qualquer chamada para esse método irá aguardar automaticamente.

Document doc = DownloadDocumentAsync();

Agora, se você quiser "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 de método simples significa o que geralmente 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 método async , a única maneira de você poder chamar um método [AutoAwait] é prefixando-o com start . Isso força você a escrever código que tenha o mesmo significado, independentemente de aparecer em um método async ou não.

Então eu começo a ficar ganancioso! :)

Primeiramente, quero que async se aplique aos métodos de interface:

interface IThing
{
    async int GetCount();
} 

Isso basicamente significa que o método de implementação deve retornar Task<int> ou algo compatível com await e os chamadores do método obterão [AutoAwait] de comportamento.

Além disso, quando eu implementar o método acima, quero poder escrever:

async int GetCount()

Portanto, não preciso mencionar Task<int> como o tipo de retorno.

Além disso, quero que async se aplique a tipos delegados (que, afinal, são como interfaces com um método). Então:

public async delegate TResult AsyncFunc<TResult>();

Um delegado async tem - você adivinhou - [AutoAwait] behavior. De um método async , você pode chamá-lo e ele será automaticamente await ed (a menos que você escolha apenas start it). E se você disser:

AsyncFunc<Document> getDoc = DownloadDocumentAsync;

Isso simplesmente funciona. Não é uma chamada de método. Nenhuma tarefa foi iniciada ainda - um async delegate não é uma tarefa. É uma fábrica para fazer tarefas. Você pode dizer:

Document doc = getDoc();

E isso iniciará uma tarefa e aguardará o término e o resultado. Ou você pode dizer:

Task<Document> t = start getDoc();

Portanto, um local em que o "encanamento" vaza é que, se você deseja tornar um delegado para um método async , é necessário saber usar um tipo async delegate . Então, em vez de Func , você deve dizer AsyncFunc e assim por diante. Embora um dia esse tipo de coisa possa ser consertado por melhor inferência de tipos.

Outra questão é o que deve 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.

    
por 30.10.2010 / 19:30
fonte
4
hearken unto AsyncFetch(…)

(se você não entender, leia a entrada de blog . Pelo menos é melhor que for sooth Romeo wherefore art thou AsyncFetch(…) )

    
por 29.10.2010 / 23:04
fonte
4

Eu acho que async é bom, mas talvez seja porque eu o associo a páginas assíncronas do ASP.NET - a mesma ideia.

Para a palavra-chave await , prefiro continue after ou resume after .

Eu não gosto de yield ou qualquer uma de suas variantes, porque a semântica é tal que o método nunca pode realmente render a execução; isso depende do estado da tarefa.

    
por 29.10.2010 / 23:16
fonte
3

Adicionei comentários no blog do Eric também, não vejo problemas em usar a mesma palavra-chave async

var data = async DownloadFileAsync(url);

Estou apenas expressando que desejo baixar o arquivo de forma assíncrona. Há um pouco de redundância aqui, "async" aparece duas vezes, porque também está no nome do método. O compilador pode ser mais inteligente e detectar a convenção de que os métodos que terminam em "Async" são métodos assíncronos infact e adicionam isso a nós no código compilado. Então, ao invés disso, você pode apenas querer ligar

var data = async DownloadFile(url);

ao invés de chamar o síncrono

var data = DownloadFile(url);

Heck, nós também poderemos defini-los da mesma maneira, já que a palavra-chave async está lá em nossa declaração, por que devemos adicionar manualmente "Async" a cada nome de método - o compilador pode fazer isso por nós.

    
por 30.10.2010 / 02:00
fonte
3

async = task - Está modificando uma função para retornar uma tarefa, então por que não usar a palavra-chave "task"?

await = finish - Não precisamos necessariamente esperar, mas a tarefa precisa ser "concluída" antes de usar o resultado.

    
por 07.11.2010 / 03:28
fonte
2

Eu gosto de yield until . yield while , já sugerido, é ótimo e não apresenta nenhuma palavra-chave nova, mas acho que "até" captura o comportamento um pouco melhor.

Acho que yield <something> é uma ótima ideia, porque o rendimento já captura a ideia de tornar o restante do método uma continuação tão bem. Talvez alguém possa pensar em uma palavra melhor do que "até".

    
por 30.10.2010 / 02:23
fonte
2

Eu só quero registrar meu voto para a sugestão de comefrom de Aaron G - o primeiro uso apropriado que eu vi do INTERCAL Declaração COMEFROM . A idéia é que é o oposto do GOTO (saltar da declaração GOTO), fazendo com que algum lugar no código salte para a instrução COMEFROM.

    
por 30.10.2010 / 07:09
fonte
2

Como estamos lidando com Task<T> s, que tal usar start como palavra-chave antes da declaração, como em:

start var document = FetchAsync(urls[i]);

    
por 05.11.2010 / 19:22
fonte
1

É interessante notar que o F # também usa a palavra-chave async em seus fluxos de trabalho assíncronos, que é praticamente a mesma coisa que a nova funcionalidade assíncrona no C # 5. Portanto, eu manteria esse o mesmo

Para a palavra-chave await em F #, eles usam apenas let! em vez de let . C # não tem a mesma sintaxe de atribuição, então eles precisam de algo do lado direito do sinal = . Como Benjol disse, funciona da mesma forma que yield , então deveria ser uma variante disso.

    
por 30.10.2010 / 01:50
fonte
1

yield async FetchAsync(..)

Isso funciona perfeitamente com o modificador async que você precisa colocar no método que está invocando. E também a semântica do atual yield return , ou seja, você retorna e produz a execução para o código de enumeração, enquanto neste caso você está produzindo sua execução para o método assíncrono.

Imagine que, no futuro, haverá outros usos para yield , poderíamos adicionar um yield x , em que x é o novo recurso brilhante, em vez de ter todas essas palavras-chave diferentes para executar a mesma coisa.

Francamente, eu não entendo muito bem o argumento 'não ceder a execução'. Afinal de contas, não é o ponto de chamar outro método já "render a execução" para esse método? Independentemente de ser assíncrono ou não? Estou faltando alguma coisa aqui?

E bom para você se o async retornar de forma síncrona, mas com a palavra-chave deve indicar que há uma probabilidade de que o método seja executado de forma assíncrona e que você esteja produzindo a execução para outro método. Seu método deve considerar isso independentemente de o método realmente fazer chamadas assíncronas ou não.

IMO Eu acho que os vários casos de 'não render' são um detalhe de implementação. Eu prefiro garantir a consistência na linguagem (ou seja, reutilizando yield ).

    
por 31.10.2010 / 20:11
fonte
0

Que tal complete , como em "Eu quero concluir a tarefa"?

Task<byte[]> downloadTask = DownloadFileAsync(url);
byte[] data = complete downloadTask;
    
por 30.10.2010 / 01:50
fonte
0

task (para declaração de método) e async (dentro do corpo do método)

    
por 24.01.2012 / 05:08
fonte

Tags