Quando você tem uma chamada assíncrona, por exemplo. async.each
, e durante esse tempo de execução, o array que é passado tem modificações, então pode nunca terminar ou irá chamar o callback duas vezes.
Exemplo:
async = require "async"
arr = [1, 2, 3]
async.each arr, ((i, cb) -> console.log "i"; setImmediate(cb)), (err) -> console.log "done"
arr.push(4)
Este exemplo percorre os 3 elementos da matriz original e imprime i
mas nunca chama o retorno de chamada, porque em async.each
está fazendo:
if (completed >= arr.length) {
callback(null);
}
Ao olhar para o código assíncrono, ele está fazendo uma comparação com arr.length
que pode mudar ... não seria melhor armazenar o comprimento do array original e fazer uma comparação com ele, garantindo que o retorno de chamada concluído será chamado ?
Violino:
http://jsfiddle.net/4ysKX/1/
Você está certo - então não o modifique. Clone sua matriz antes de passá-la para uma função assíncrona se precisar alterá-la mais tarde.
@aearly Sim, isso é uma
Seria difícil se proteger contra _qualquer_ modificação de array sem clonar a entrada, por exemplo, uma modificação síncrona na função do iterador, mas a proteção contra modificação _assíncrona_ não relacionada à iteração deve ser direta, mesmo sem clone - na verdade, apenas não assumindo arr.length
permanecerá constante após girar os trabalhos assíncronos (salvando o comprimento em uma variável antes da iteração).
(Eu trabalho com @bradens - um de nós enviará um PR com testes, se for bem-vindo).
Um RP com testes seria bem-vindo, mas, em última análise, cabe a @caolan fazer a fusão.
A desvantagem de fazer isso é a sobrecarga extra da cópia do array. Se você tiver um array grande ou estiver chamando async.each
et al muitas vezes, será mais lento e usará mais memória.
Eu concordo que uma cópia do array seria uma sobrecarga muito grande. Estou sugerindo que, como a iteração inicial do array é síncrona, o comprimento do array original pode ser salvo para verificação posterior. Dessa forma, o número de chamadas de retorno corresponderá ao número de chamadas de iterador, mesmo se a matriz for alterada nesse meio tempo. Isso deve ser fácil e zero sobrecarga para async.each
, mas eu não olhei para as outras funções ainda. Seria bom se todas as funções paralelas tivessem um comportamento consistente nesses casos.
Esta é uma duplicata de # 557.
Async acabou desautorizando modificações de array depois do fato? Eu tenho um caso de uso em que seria bom modificar o array original após o fato, de modo a iterar em mais elementos do que inicialmente.
Comentários muito úteis
Async acabou desautorizando modificações de array depois do fato? Eu tenho um caso de uso em que seria bom modificar o array original após o fato, de modo a iterar em mais elementos do que inicialmente.