Ahora que sabemos lo que está reservado para c # 5, aparentemente todavía hay una oportunidad para que podamos influir en la elección de las dos nuevas palabras clave para ' Asincronía ' que Anders Heijsberg anunció ayer en 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 tiene una explicación de la elección de las dos palabras clave actuales, y la forma en que han sido mal entendidas en los estudios de usabilidad. Los comentarios tienen varias otras proposiciones.
Por favor, una sugerencia por respuesta, se duplicarán los duplicados.
Respuestas:
Dado que no tengo claro el significado / necesidad de
async
, realmente no puedo discutirlo, pero mi mejor sugerencia para reemplazarawait
es:yield while
(mira! no hay nuevas palabras clave)Tenga en cuenta que después de haber pensado en esto un poco más, me pregunto si reutilizar de
while
esta manera es una buena idea; la tendencia natural sería esperar un booleano después.(Piensa: encontrar buenas palabras clave es como encontrar buenos nombres de dominio :)
fuente
while(x) {...}
ninguno, six
es falso.while
. Si agrega un verbo, likedo
, entonces obtienedo {...} while (x)
, que ejecuta el cuerpo independientemente de x (al menos una vez). Su sugerencia deyield while
parece muy similar ado while
, pero con garantías opuestas de realizar el verbo, lo que puede ser un poco engañoso (pero no es un gran problema). Lo que más me disgustayield
es que implica la implementación de un mecanismo. El punto deasync
/await
es que escribes una operación asincrónica en un estilo sincrónico.yield
rompe ese estilo sincrónico.await
palabra clave sería reconocida por el contexto, por lo que aún podría tener un método o variable llamada "esperar" si lo desea. Hasta cierto punto, creo que usar una nueva palabra clave para una nueva funcionalidad es menos confuso que reutilizar una palabra clave existente para significar más de una cosa. (ejemplo exagerado: dangermouse.net/esoteric/ook.html )¿Qué tal no tener una palabra clave?
Me gustaría que el compilador se dé cuenta de que, la mayoría de las veces, cuando llamo a un método asincrónico, quiero el resultado.
Eso es. La razón por la que las personas están teniendo dificultades para pensar una palabra clave para esto es porque es como tener una palabra clave para "hacer lo que haría si las cosas fueran perfectamente normales". Esa debería ser la predeterminada, no requiere una palabra clave.
Actualizar
Originalmente sugerí que el compilador debería ser inteligente con la inferencia de tipos para descubrir qué hacer. Pensando más en esto, mantendría la implementación existente en el CTP tal como es, pero haré un par de adiciones triviales, para reducir los casos en los que necesitaría usar la
await
palabra clave explícitamente.Nos inventamos un atributo:
[AutoAwait]
. Esto solo se puede aplicar a los métodos. Una forma de aplicar esto a su método es marcarloasync
. Pero también puede hacerlo a mano, por ejemplo:Luego, dentro de cualquier
async
método, el compilador asumirá que desea esperar una llamadaDownloadDocumentAsync
, por lo que no necesita especificarlo. Cualquier llamada a ese método lo esperará automáticamente.Ahora, si desea "ser inteligente" y obtener el
Task<Document>
, utilice un operadorstart
, que solo puede aparecer antes de una llamada al método:Aseado, creo. Ahora, una llamada de método simple significa lo que generalmente significa: esperar a que se complete el método. E
start
indica algo diferente: no esperes.Para el código que aparece fuera de un
async
método, la única forma en que puede llamar a un[AutoAwait]
método es mediante el prefijostart
. Esto lo obliga a escribir código que tenga el mismo significado independientemente de si aparece en unasync
método o no.¡Entonces empiezo a ponerme codicioso! :)
En primer lugar, quiero
async
aplicar a los métodos de interfaz:Básicamente significa que el método de implementación debe regresar
Task<int>
o algo compatibleawait
, y las personas que llaman al método obtendrán un[AutoAwait]
comportamiento.Además, cuando implemente el método anterior, quiero poder escribir:
Así que no tengo que mencionar
Task<int>
como tipo de retorno.Además, quiero
async
aplicar a los tipos de delegados (que, después de todo, son como interfaces con un método). Entonces:Un
async
delegado tiene, lo has adivinado,[AutoAwait]
comportamiento. Desde unasync
método puede llamarlo y se editará automáticamenteawait
(a menos que elija simplementestart
). Y entonces si dices:Eso solo funciona. No es una llamada al método. Aún no se ha iniciado ninguna tarea, y
async delegate
no es una tarea. Es una fábrica para hacer tareas. Puedes decir:Y eso comenzará una tarea y esperará a que termine y le dará el resultado. O puedes decir:
Entonces, un lugar en este lugar donde se filtra la "plomería" es que si desea hacer un delegado a un
async
método, debe saber usar unasync delegate
tipo. Entonces, en lugar deFunc
usted debe decirAsyncFunc
, y así sucesivamente. Aunque algún día ese tipo de cosas podría solucionarse mediante una mejor inferencia de tipos.Otra pregunta es qué debería suceder si dice comenzar con un método ordinario (no asíncrono). Obviamente, un error de compilación sería la opción segura. Pero hay otras posibilidades.
fuente
var
, potencialmente teniendo que sustituir algún nombre de tipo largo y explícito, y también es ambiguo entre elawait
caso y el caso en el que alguien accidentalmente llamó al método asíncrono en lugar del método sincrónico normal. Parece intuitivo al principio, pero en realidad viola el principio de la menor sorpresa.var
? Me pregunto si estás respondiendo a la revisión anterior de mi respuesta ... Lo he reescrito completamente. Ahora puede pensar en esta sugerencia de la siguiente manera: si el método está marcado con un atributo especial, es como si laawait
palabra clave se inserta automáticamente delante de las llamadas a ese método (a menos que suprima esto con elstart
prefijo). Todo permanece exactamente como en el CTP y, por lo tanto,var
funciona bien.public async Task<int> FooAsync()
.(si no lo entiendes, lee la entrada del blog de Eric . Al menos es mejor que
for sooth Romeo wherefore art thou AsyncFetch(…)
)fuente
Creo que
async
está bien, pero tal vez sea porque lo asocio con las páginas asincrónicas de ASP.NET, la misma idea.Para la
await
palabra clave prefierocontinue after
oresume after
.No me gusta
yield
ni ninguna de sus variantes, porque la semántica es tal que el método nunca puede generar ejecución; Depende del estado de la tarea.fuente
resume after
paraawait
. Tal vezasync
podría ser llamadoresumable
.after
, elcontinue after
enfoque tiene una gran ventaja de implementación: incluye una palabra clave contextual actualmente existente, pero con una sintaxis que no es compatible con el uso actual. Eso garantiza que la adición nunca romperá el código existente. Cuando se usa una palabra clave completamente nueva, la implementación debe hacer frente a los posibles usos de la palabra como un identificador en el código anterior, lo que puede ser bastante complicado.También agregué comentarios en el blog de Eric, no veo ningún problema con el uso de la misma palabra clave
async
Solo estoy expresando que quiero descargar el archivo de forma asincrónica. Aquí hay un poco de redundancia, "async" aparece dos veces, porque también está en el nombre del método. El compilador podría ser más inteligente y detectar la convención de que los métodos que terminan en "Asíncrono" son métodos asíncronos de hecho, y agregarlo para nosotros en el código compilado. Entonces, en vez de eso, solo querrás llamar
en lugar de llamar al síncrono
Diablos, también deberíamos poder definirlos de la misma manera, ya que la palabra clave asincrónica está allí en nuestra declaración, ¿por qué debemos agregar manualmente "Async" a cada nombre de método? El compilador puede hacerlo por nosotros.
fuente
async
palabra clave en el método es simplemente agradable (si lo he entendido correctamente), me pregunto si lo mejor no sería hacer lo contrario de lo que sugiere: abandonarasync
el método y simplemente usarlo donde tienen actualmenteawait
.async Task<Byte[]> DownloadFile(...)
lugar deTask<Byte[]> DownloadFileAsync(...)
(este último será la firma compilada de todos modos). De cualquier manera funciona.async = task : está modificando una función para devolver una tarea, entonces, ¿por qué no usar la palabra clave "task"?
await = terminar - No necesariamente tenemos que esperar, pero la tarea tiene que "terminar" antes de usar el resultado.
fuente
Me gusta
yield until
.yield while
, ya sugerido, es excelente y no introduce palabras clave nuevas, pero creo que "hasta" captura el comportamiento un poco mejor.Creo que
yield <something>
es una gran idea, porque el rendimiento ya captura la idea de hacer que el resto del método sea una continuación tan bien. Quizás alguien pueda pensar en una palabra mejor que "hasta".fuente
Solo quiero registrar mi voto para la sugerencia de Aaron G del
comefrom
primer uso apropiado que he visto de la declaración COMEFROM de INTERCAL . La idea es que es algo opuesto a GOTO (saltando de la declaración GOTO) en que hace que algún lugar en su código salte a la declaración COMEFROM.fuente
Como estamos tratando con
Task<T>
s, ¿qué tal si usamosstart
como palabra clave antes de la declaración, como en:start var document = FetchAsync(urls[i]);
fuente
finish
sería incluso mejor questart
?Vale la pena señalar que F # también usa la
async
palabra clave en sus flujos de trabajo asíncronos, que es casi exactamente lo mismo que la nueva funcionalidad asíncrona en C # 5. Por lo tanto, lo mantendría igualPara la
await
palabra clave en F #, solo usan enlet!
lugar delet
. C # no tiene la misma sintaxis de asignación, por lo que necesitan algo en el lado derecho del=
signo. Como dijo Benjol, funciona igualyield
que casi debería ser una variante de eso.fuente
do!
, pero sabías que ...yield async FetchAsync(..)
Esto va perfectamente con el
async
modificador que necesita para poner el método que está invocando. Y también la semántica de la corrienteyield return
, es decir, devuelve y produce la ejecución del código de enumeración, mientras que en este caso está cediendo su ejecución al método asincrónico.Imagínese si en el futuro habrá otros usos para
yield
, podríamos agregar unyield x
donde x es la nueva característica brillante en lugar de tener todas estas palabras clave diferentes para hacer principalmente la misma cosa, rendimiento de ejecución.Francamente, no entiendo muy bien el argumento de 'no ceder ejecución'. Después de todo, ¿el punto de llamar a otro método ya no es 'ceder la ejecución' de ese método? ¿Independientemente de si es asíncrono o no? ¿Me estoy perdiendo algo aquí?
Y es bueno para usted si el
async
retorno es sincrónico, pero tener la palabra clave allí debería significar que existe una posibilidad probable de que el método se ejecute de forma asincrónica y que esté ejecutando la ejecución a otro método. Su método debe tener en cuenta eso independientemente de si el método realmente hace llamadas asincrónicas o no.En mi opinión, los diversos casos de 'no rendir' son un detalle de implementación. Prefiero garantizar la coherencia en el lenguaje (es decir, reutilizar
yield
).fuente
¿Qué
complete
tal, como en "Quiero completar la tarea"?fuente
task
(para la declaración del método) yasync
(dentro del cuerpo del método)fuente