2012-11-28 16 views
20

Mi stavo chiedendo se qualcuno sa come implementare setTimeout in node.js. Credo di aver letto da qualche parte che questo non fa parte del V8. Ho cercato rapidamente di trovare l'implementazione, ma non sono riuscito a trovarla nella fonte (BIG). Ad esempio, ho trovato questo file timers.js, che quindi ad esempio collega a timer_wrap.cc. Ma questi file non rispondono completamente a tutte le mie domande.Come viene implementato setTimeout in node.js

  • L'implementazione di V8 è setTimeout? Immagino anche dalla fonte che la risposta sia no.
  • Come viene implementato setTimeout? javascript o nativo o combinazione di entrambi? Da timers.js Presumo qualcosa lungo la linea di entrambi:

    var Timer = process.binding('timer_wrap').Timer;` 
    
  • Quando si aggiungono più timer (setTimeout) come fa sapere node.js cui eseguire prima? Aggiunge tutti i timer a una raccolta (ordinata)? Se è ordinato, trovare il timeout che deve essere eseguito è O (1) e O (log n) per l'inserimento? Ma poi di nuovo in timers.js li vedo usare una lista collegata?

  • Ma poi di nuovo aggiungere un sacco di timer non è un problema?
  • Durante l'esecuzione di questo script:

    var x = new Array(1000), 
        len = x.length; 
    
    /** 
    * Returns a random integer between min and max 
    * Using Math.round() will give you a non-uniform distribution! 
    */ 
    function getRandomInt (min, max) { 
        return Math.floor(Math.random() * (max - min + 1)) + min; 
    } 
    
    var y = 0; 
    
    for (var i = 0; i < len; i++) { 
        var randomTimeout = getRandomInt(1000, 10000); 
    
        console.log(i + ', ' + randomTimeout + ', ' + ++y); 
        setTimeout(function() { 
         console.log(arguments); 
        }, randomTimeout, randomTimeout, y); 
    } 
    

    si ottiene un po 'di utilizzo della CPU, ma non più di tanto?

  • Mi chiedo se implementare tutte queste richiamate una alla volta in una lista ordinata se otterrò prestazioni migliori?

risposta

17

Hai già fatto gran parte del lavoro. V8 non fornisce un'implementazione per setTimeout perché non fa parte di ECMAScript. La funzione che usi è implementata in timers.js, che crea un'istanza di un oggetto Timeout che è un wrapper attorno a una classe C.

C'è un commento nella fonte che descrive come stanno gestendo i timer.

// Because often many sockets will have the same idle timeout we will not 
// use one timeout watcher per item. It is too much overhead. Instead 
// we'll use a single watcher for all sockets with the same timeout value 
// and a linked list. This technique is described in the libev manual: 
// http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts 

Il che indica che sta utilizzando un doppio elenco collegato che è il n. 4 nell'articolo collegato.

Se non c'è una richiesta, ma molte migliaia (milioni ...), tutto impiegando un qualche tipo di timeout con lo stesso valore di timeout, allora si può fare ancora meglio:

All'avvio il timeout, calcolare il valore di timeout e inserire il timeout alla fine dell'elenco.

Quindi utilizzare un ev_timer per attivare quando è atteso il timeout all'inizio dell'elenco (ad esempio, utilizzando la tecnica # 3).

Quando v'è una certa attività, rimuovere il timer dalla lista, ricalcolare i timeout, aggiungere alla fine della lista di nuovo, e assicurarsi di aggiornare l'ev_timer se fosse stata scattata da inizio del elenco.

In questo modo, si può gestire un numero illimitato di timeout a O (1) tempo per avviare, fermare e aggiornamento dei temporizzatori, a scapito di una maggiore complicazione , e dover utilizzare un timeout costante. Il timeout costante assicura che l'elenco rimanga ordinato.

Node.js è progettato attorno alle operazioni asincrone e setTimeout è una parte importante di questo. Non proverei a diventare complicato, basta usare quello che forniscono. Fidati che è abbastanza veloce finché non hai dimostrato che nel tuo caso specifico si tratta di un collo di bottiglia. Non rimanere bloccato sull'ottimizzazione prematura.

UPDATE

Quello che succede è che hai essenzialmente un dizionario di timeout al livello più alto, in modo che tutti 100ms timeout sono raggruppati insieme. Ogni volta che viene aggiunto un nuovo timeout o il trigger di timeout più vecchio, viene aggiunto all'elenco. Ciò significa che il timeout più vecchio, quello che si innescherà il più presto, è all'inizio della lista. C'è un solo timer per questo elenco, ed è impostato in base al tempo fino alla scadenza del primo elemento nell'elenco.

Se si chiama setTimeout 1000 volte ciascuno con lo stesso valore di timeout, questi verranno aggiunti all'elenco nell'ordine definito setTimeout e non è necessario alcun ordinamento. È una configurazione molto efficiente.

+0

in questo esempio i timeout sono sempre gli stessi? 60 secondi? – Alfred

+0

Quindi vuoi che succeda un numero di cose in 60 secondi, e stai cercando di capire se raggruppare tutte queste cose insieme o creare un setTimeout per ognuna separatamente? –

+0

no spiacenti. Quel documento che hai collegato ha 60 secondi di ritardo. I miei ritardi possono essere qualsiasi cosa e molto! – Alfred

3

Nessun problema con molti timer! Durante il polling di chiamata del ciclo uv, passa l'argomento di timeout ad esso con il timer più vicino di tutti i timer.

[vicina timer di tutti i timer]
https://github.com/joyent/node/blob/master/deps/uv/src/unix/timer.c # 120

RB_MIN(uv__timers, &loop->timer_handles) 

[pass argomento timeout per interrogare api]
https://github.com/joyent/node/blob/master/deps/uv/src/unix/core.c # 276

timeout = 0; 
if ((mode & UV_RUN_NOWAIT) == 0) 
    timeout = uv_backend_timeout(loop); 

uv__io_poll(loop, timeout); 

Nota: su Sistema operativo Windows, è quasi la stessa logica

Problemi correlati