Async: Suporte à função `async`

Criado em 16 mar. 2017  ·  10Comentários  ·  Fonte: caolan/async

Um dos elefantes na sala é o novo suporte async / await que chegou ao Node e ao Chrome e em breve chegará a todos os outros navegadores principais. Eu estive pensando sobre o que o Async pode fazer no mundo async / await .

Atualmente, podemos adaptar as funções async envolvendo-as com asyncify . Como uma função async é essencialmente apenas uma função que retorna uma Promise, esse adaptador antigo pode convertê-la facilmente em uma função de estilo de retorno de chamada. No entanto, isso leva à aparência um tanto absurda:

async.mapLimit(arr, 10, async.asyncify(async (val) => {
  let foo = await doSomething(val);
  //...
 return bar;
}), done);

No entanto, um dos recursos na especificação das funções async é que:

Object.getPrototypeOf(asyncFn)[Symbol.toStringTag] === "AsyncFunction"

Isso fornece uma maneira de detectar facilmente as funções async (nativas). Poderíamos usar esta técnica para automaticamente asyncify eles. O exemplo acima fica:

async.mapLimit(arr, 10, async (val) => {
  let foo = await doSomething(val);
  //...
 return bar;
}, done);

...que parece fluir muito mais naturalmente. Também acho que devemos continuar a usar retornos de chamada. Se um usuário quisesse await o resultado, ele teria que promisify a função, ou pify Async como um todo:

let result = await pify(async.mapLimit)(arr, 10, async (val) => {
  let foo = await doSomething(val);
  //...
 return bar;
});

O método acima para detectar funções async só funciona com funções nativas. Eu não acho que exista uma maneira de detectar funções transpiladas do Babel. Certamente não podemos detectar funções normais que simplesmente retornam Promises, porque teríamos que retroativamente não passar um callback. Haveria uma grande ressalva de que isso só funcionaria sem um transpilador em ambientes muito modernos, caso contrário, você ainda precisa envolver manualmente com asyncify .

Além disso, reconhecidamente, muitos métodos Async não fazem sentido com async / await . A maioria dos métodos de fluxo de controle (exceto por coisas como auto e queue ) são replicados mais facilmente com construções de fluxo de controle nativo. map e parallel podem ser substituídos por Promise.map e Promise.all . No entanto, as funções de coleta limitadoras seriam muito úteis, assim como auto e algumas outras. (Além disso, autoInject com funções async é um sonho de fluxo de controle assíncrono!)

enhancement feedback-wanted

Comentários muito úteis

Existe uma razão para fazer Object.getPrototypeOf(asyncFn)[Symbol.toStringTag] === "AsyncFunction" ou podemos fazer asyncFn[Symbol.toStringTag] === "AsyncFunction" (parece funcionar em FF)?

Essa é apenas a maneira canônica de especificação ECMA de fazer isso. Acho que, em teoria, alguém poderia substituir asyncFn[Symbol.toStringTag] .

Assim é a proposta sempre que alguém fornecer um retorno de chamada no formato cb(err, arg) devemos detectar se é um AsyncFunction; se for uma função assíncrona, devemos aplicar promisify, caso contrário, use-a como está

Eu acho que você tem isso um pouco para trás. Sempre que aceitamos uma função iteratee de aceitação de retorno de chamada ( function(args..., callback) {} ), devemos verificar se é uma função async e, em seguida, asyncify .

O exemplo await é o que alguém faria se quisesse await um método assíncrono. Eu não acho que devemos fazer com que os métodos Async comecem a retornar promessas para que isso funcione - deixe isso para o usuário fazer.

Todos 10 comentários

Vou refletir um pouco mais sobre isso, mas gostaria de fazer algumas perguntas técnicas apenas para tentar visualizar melhor como é isso.

Existe uma razão para fazer Object.getPrototypeOf(asyncFn)[Symbol.toStringTag] === "AsyncFunction" ou podemos fazer asyncFn[Symbol.toStringTag] === "AsyncFunction" (parece funcionar em FF)?

Assim é a proposta toda vez que alguém fornecer um retorno de chamada no formato cb(err, arg) devemos detectar se é um AsyncFunction ; se for uma função assíncrona, devemos aplicar promisify caso contrário, use-a como está

Também desculpe, não estou seguindo o exemplo await , se detectarmos que a função é AsyncFunction quais são os desafios de suportar await ?

Existe uma razão para fazer Object.getPrototypeOf(asyncFn)[Symbol.toStringTag] === "AsyncFunction" ou podemos fazer asyncFn[Symbol.toStringTag] === "AsyncFunction" (parece funcionar em FF)?

Essa é apenas a maneira canônica de especificação ECMA de fazer isso. Acho que, em teoria, alguém poderia substituir asyncFn[Symbol.toStringTag] .

Assim é a proposta sempre que alguém fornecer um retorno de chamada no formato cb(err, arg) devemos detectar se é um AsyncFunction; se for uma função assíncrona, devemos aplicar promisify, caso contrário, use-a como está

Eu acho que você tem isso um pouco para trás. Sempre que aceitamos uma função iteratee de aceitação de retorno de chamada ( function(args..., callback) {} ), devemos verificar se é uma função async e, em seguida, asyncify .

O exemplo await é o que alguém faria se quisesse await um método assíncrono. Eu não acho que devemos fazer com que os métodos Async comecem a retornar promessas para que isso funcione - deixe isso para o usuário fazer.

Implementado em #1390!

Esta foi uma mudança importante e quebrou nosso código implantado. Por favor, pense duas vezes ao fazer essas coisas sem aumentar a versão principal.

PS: obrigado por todo o ótimo trabalho que você está fazendo com esta biblioteca 😄

Atirar! O que quebrou. Você poderia criar um ticket com detalhes do
ambiente em que isso não funcionou?
Obrigado!

Quarta-feira, 5 de abril de 2017 às 10h18 Manuel Valls Fernández <
[email protected]> escreveu:

Esta foi uma mudança importante e quebrou nosso código implantado. Por favor, pense duas vezes
ao fazer essas coisas sem aumentar a versão principal.

PS: obrigado por todo o ótimo trabalho que você está fazendo com esta biblioteca 😄


Você está recebendo isso porque comentou.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/caolan/async/issues/1386#issuecomment-291875817 ou silenciar
o segmento
https://github.com/notifications/unsubscribe-auth/ADUIEPNkTSOVuuiwucBVrH983X6B568Wks5rs6K3gaJpZM4Mf64R
.

Concordo, quebrou minha construção também ...

Uma cascata que estava chamando uma função assíncrona que funcionou alguns dias atrás começou a falhar com um "cb não é função" porque o retorno de chamada assíncrono não era mais fornecido à função.

Desculpe, quebramos seu código. Não antecipamos seu caso de uso para funções async . Eu recomendaria reverter para 2.2.0 ou refatorar seu código para return os valores que você precisa, em vez de usar retornos de chamada. Infelizmente, o gato está fora do saco com esse recurso, então não podemos reverter.

@aearly Por favor, não mencione isso!! É muito legal da sua parte responder :1st_place_medal:

@manvalls me sugeriu uma ótima solução que não exigia reversão. Como você está usando o símbolo para detectar o async na declaração da função, ele pensou em uma maneira inteligente de enganar a detecção.

Minha cascata estava usando funções exportadas de outros módulos, um deles era async e assim causando a falha.

Então, apenas mudando de:

... 
/* services module */
function doThis(param, cb) {
...
}

async function doThatAsync(param, cb) {
...
}

module.exports = {
  doThis: doThis,
  doThat: doThatAsync  
}; 

...
async.waterfall([
  services.doThis,
  services.doThat,  // fails with "cb is not a function"
], err => {
...
}

Para:

... 
/* services module */
function doThis(param, cb) {
...
}

async function doThatAsync(param, cb) {
...
}

module.exports = {
  doThis: doThis,
  doThat: (...args) => doThatAsync(..args)   // cheating the detection
}; 

...
async.waterfall([
  services.doThis,
  services.doThat, /* it works!!! */
], err => {
...
}

Muito obrigado novamente

podemos usar async/await com async.autoInject()?

async.autoInject({

    conn1: async function () {
      return conn1;
    },

    conn2: async function () {
      return conn2;
    },
});

parece não funcionar, eu recebo:

Erro: as funções de tarefa autoInject requerem parâmetros explícitos.
em /Users/alexamil/WebstormProjects/nabisco/cdt-now/node_modules/async/dist/async.js:2081:23
em /Users/alexamil/WebstormProjects/nabisco/cdt-now/node_modules/async

@ORESoftware sim, as funções async devem funcionar com autoInject . Testei o código que você postou no Chrome e funcionou. Recebi um ReferenceError no callback final, pois conn1 e conn2 são undefined . Depois de alterá-lo para

async.autoInject({
    conn1: async function () {
      return 'foo'
    },
    conn2: async function () {
      return 'bar'
    },
})

Funciona bem. No entanto, não suportamos funções async transpiladas. Você está transpilando seu código?

Esta página foi útil?
0 / 5 - 0 avaliações