2015-07-23 15 views
9

Il mio ex capo aveva un weird bug where when he used setInterval with a long delay interval:Qual è la differenza tra setInterval (func) e setInterval (function() {func()})

setInterval(func, 3000000 /*50 minutes*/); 

Node.js si è schiantato.

func può essere anche una semplice funzione che è semplicemente console.log('something').

Qualcuno gli ha suggerito di avvolgere una funzione anonima intorno a func e in realtà ha risolto il problema.

Per quanto ne so, non dovrebbe fare la differenza e anche considerato una cattiva pratica almeno nel javascript dei browser.

C'è una differenza di Node.js tra

  • setInterval(func, delay)
  • setInterval(function(){func()}, delay)

o è un bug in Node.js?


UPDATE:

Bug report on GitHub

+0

Non vedo perché sarebbe considerata una cattiva pratica avere una funzione anonima invece di un nome di funzione ... Curioso cosa hanno da dire gli altri. Prenderò in considerazione che i due esempi hanno lo stesso risultato – mplungjan

+0

@mplungjan, naturalmente è una cattiva pratica in JavaScript generico, è una funzione senza motivo e in genere è un'indicazione di qualcuno che non capisce veramente le funzioni di prima classe in JS. –

+1

Mi chiedo che 'func' non debba essere raccolto qui perché il riferimento in' setInterval' in qualche modo non conta ... Molto buona domanda. – raina77ow

risposta

0

E 'possibile che V8 sta facendo la raccolta dei rifiuti dietro le quinte, eliminando qualsiasi funzione che stai riferimento qui come "func". Ciò lascerebbe "func" che punta a una funzione non valida.

Provare a eseguire nodo con il seguente argomento:

node --expose-gc app.js 

... e poi nel codice dopo il setInterval (func, 10000) chiamata:

global.gc(); 

... che si innescherà la garbage collection in V8. Dato che ho abbassato il timer fino a dieci secondi, puoi testare la teoria senza aspettare un'ora intera.

È completamente possibile che il wrapping di una chiamata di funzione anonima impedisca quindi alla raccolta di dati inutili di rimuovere la funzione effettiva dalla memoria.

Nota che si può anche iniziare con:

node --trace-gc app.js 

... che sarà il login garbage collection dal V8. Potrebbe dirti qualcosa di interessante. Vedi https://groups.google.com/forum/#!topic/nodejs/gITsLR7Zkew

+0

Non vedo perché questo potrebbe accadere. C'è ancora un riferimento a 'createSasTokenTimer'. Quando viene chiamato 'setInterval', crea un'istanza' timer' che ha una proprietà chiamata '_onTimeout'. Una funzione interna chiamata 'wrapper' è assegnata a' _onTimeout', e 'wrapper' stesso chiama il' callback', che è il callback passato in 'setInterval', che in definitiva è' createSasTokenTimer'. Anche se il contesto principale è disposto, c'è ancora un riferimento e quindi il callback è ancora "vivo". Inoltre, non spiega perché la funzione anonima non finisca per ricevere GC. –

+0

Immagino qui che se lo avvolgi in una chiamata anonima, allora sta andando in un percorso diverso, per esempio, è nel threadpool invece di essere eseguito nel thread principale. –

+0

C'è un solo thread nel nodo. Ma questo è oltre il punto perché non ha nulla a che fare con il threading. L'OP si chiede perché il callback finisca per essere indefinito dopo un po 'quando viene passato come riferimento a una dichiarazione di funzione, a patto che venga passato come riferimento a una funzione anonima. Dal punto di vista di 'setInterval', non c'è alcuna differenza in quanto ottiene un riferimento a una funzione in entrambi i casi. –

1

Per indirizzare direttamente la domanda: Sì, c'è una differenza. Non nella funzionalità di setInterval, ma nell'ambito della funzione che avrà quando viene eseguito.

Scenario 1:

setInterval(function() { 
    console.trace(); 
}, 3000000); 

ogni 50 minuti, una traccia dello stack verrà stampato a consolarla. In questo caso perché la console delle funzioni.traccia è stata chiamata direttamente manterrà il contesto di console.

Scenario 2:

setInterval(console.trace, 3000000); 

Questo genera un errore perché sarà invocato con il contesto del campo di applicazione che esegue, non console. Per mantenere contesto, è possibile passare una copia rilegata della funzione:

setInterval(console.trace.bind(console), 3000000); 

Non sembra il problema che il boss aveva può essere riprodotto oggi. Quindi, come altri hanno suggerito, potrebbe essere un problema con la garbage collection. Tuttavia, sarei più incline a credere che la funzione che il tuo capo stava chiamando dipendesse da un contesto specifico mantenuto tramite una funzione anonima ma perso quando si passava una funzione non associata.