2012-03-01 21 views
110

Voglio dire, il check-out questo code:Come posso creare una funzione asincrona in Javascript?

<a href="#" id="link">Link</a> 
<span>Moving</span> 

$('#link').click(function() { 
    console.log("Enter"); 
    $('#link').animate({ width: 200 }, 2000, function() { 
     console.log("finished");    
    });  
    console.log("Exit");  
}); 

come si può vedere in consolle, la funzione "animare" è asincrona, e "forchetta" il flusso del codice di blocco di gestore di eventi. Infatti:

seguire il flusso del codice di blocco!

Se desidero creare il mio function asyncFunct() { } con questo comportamento, come posso farlo con javascript/jquery? Penso che ci sia una strategia senza l'uso setTimeout()

+0

dai un'occhiata ai sorgenti di jQuery :) – yatskevich

+0

Il mathod .animate() usa un callback. Animate chiamerà il callback quando l'animazione è completa. Se hai bisogno dello stesso comportamento di .animate(), ciò di cui hai bisogno è un callback (chiamato dalla funzione "main" dopo alcune altre operazioni). È diverso se hai bisogno di una funzione asincrona "completa" (una funzione chiamata senza bloccare il flusso di esecuzione). In questo caso è possibile utilizzare setTimeout() con un ritardo vicino allo 0. –

+0

@Fabio Buda: perché callback() dovrebbe implementare una sorta di async? Infatti, non è http://jsfiddle.net/5H9XT/9/ – markzzz

risposta

148

Non è possibile eseguire una funzione asincrona veramente personalizzata. Più volte si deve far leva su una tecnologia fornita in modo nativo, come ad esempio:

  • setInterval
  • setTimeout
  • requestAnimationFrame
  • XMLHttpRequest
  • WebSocket
  • Worker
  • Alcuni HTML5 API quali come File API, Web Databas e API
  • Technologies che supportano onload
  • ... molti altri

Infatti, per l'animazione jQuery usessetInterval.

+3

Inoltre, aggiungi lavoratori del web a quell'elenco ... –

+1

Io ero discutendo di questo con un amico ieri, questa risposta è perfetta! Capisco e posso identificare le funzioni asincrone e usarle correttamente in JS. Ma semplicemente * perché * non possiamo implementare quelli personalizzati non è chiaro per me. È come una scatola nera che sappiamo come farlo funzionare (usando, ad esempio, "setInterval") ma non possiamo nemmeno aprirlo per vedere come è fatto. Ti capita di avere più informazioni sull'argomento? –

+2

@MatheusSeleziona queste funzioni native dell'implementazione del motore javascript e l'unica cosa su cui puoi fare affidamento sono le specifiche [ad es. Timer HTML5] (http://www.w3.org/TR/2011/WD-html5-20110525/timers.html) e si fidano della natura della scatola nera che si comportano secondo le specifiche. – Spoike

3

Edit: Sono assolutamente capito la domanda. Nel browser, vorrei usare setTimeout. Se fosse importante che funzionasse in un altro thread, userei Web Workers.

+1

? Questo non fa una funzione asincrona: O – markzzz

+0

http://jsfiddle.net/5H9XT/9/ a causa della tua risposta, questo non dovrebbe funzionare ... – markzzz

53

È possibile utilizzare un timer:

setTimeout(yourFn, 0); 

(dove yourFn è un riferimento alla funzione)

o, con underscore.js:

_.defer(yourFn); 

rinvia invocando la funzione fino a quando lo stack di chiamate corrente è stato cancellato, simile all'utilizzo di setTimeout con un del ay of 0. Utile per eseguire calcoli costosi o rendering HTML in blocchi senza il calcolo dal thread dell'interfaccia utente dall'aggiornamento.

+1

Questo non funziona, la mia funzione javascript che disegna in una tela mantiene rendendo l'interfaccia utente non risponde. – gab06

+1

@ gab06 - Direi che la funzione di disegno su tela sta bloccando per i suoi buoni motivi. Dividi la sua azione in molti più piccoli e invoca ognuno di essi con un timer: vedrai che l'interfaccia in questo modo risponde ai clic del mouse, ecc. –

5

This page ti guida attraverso le basi della creazione di una funzione javascript asincrona.

Performing funzione asincrona chiama JavaScript utilizzando argomenti normalmente implica la costruzione l'argomento di espressione di SetTimeout o setInterval manualmente.

Se ciò non risolve il problema, consultare la documentazione sulla funzione animate.

22

qui si ha una soluzione semplice (altro scrivere su di esso) http://www.benlesh.com/2012/05/calling-javascript-function.html

E qui si ha al di sopra soluzione pronta:

function async(your_function, callback) { 
    setTimeout(function() { 
     your_function(); 
     if (callback) {callback();} 
    }, 0); 
} 

TEST 1 (può emettere '1 x 2 3' o ' 1 2 x 3' o '1 2 3 x'):

console.log(1); 
async(function() {console.log('x')}, null); 
console.log(2); 
console.log(3); 

TEST 2 (volontà al uscita modi 'x 1'):

async(function() {console.log('x');}, function() {console.log(1);}); 

Questa funzione viene eseguita con timeout 0 - Simulerà compito asincrono

+5

TEST 1 può effettivamente emettere solo "1 2 3 x" e TEST 2 è garantito per emettere "1 x" ogni volta. Il motivo per i risultati imprevisti in TEST 2 è perché viene chiamato 'console.log (1)' e l'output ('undefined') viene passato come secondo argomento a' async() '. Nel caso di TEST 1, penso che tu non comprenda pienamente la coda di esecuzione di JavaScript. Poiché ognuna delle chiamate a 'console.log()' si verifica nello stesso stack, 'x' è garantito per essere registrato per ultimo. Sottovalutare questa risposta per disinformazione ma non ho abbastanza rep. –

+1

@Joshua: Sembra @fider pensato per scrivere TEST 2 come: 'async (function() {console.log ('x')}, function() {console.log (1)});'. – nzn

+0

Sì @nzn e @Joshua intendevo 'TEST 2 come: async (function() {console.log ('x')}, function() {console.log (1)});' - L'ho già corretto – fider

2
Function.prototype.applyAsync = function(params, cb){ 
     var function_context = this; 
     setTimeout(function(){ 
      var val = function_context.apply(undefined, params); 
      if(cb) cb(val); 
     }, 0); 
} 

// usage 
var double = function(n){return 2*n;}; 
var display = function(){console.log(arguments); return undefined;}; 
double.applyAsync([3], display); 

Sebbene non fondamentalmente diverso rispetto alle altre soluzioni, Credo mia soluzione fa alcune ulteriori cose belle:

  • permette per i parametri alle funzioni
  • passa l'uscita della funzione di callback al
  • viene aggiunto alla Function.prototype consentendo un modo migliore per chiamarlo

Inoltre, la somiglianza con la funzione built-in Function.prototype.apply sembra appropriato per me.

0

MDN ha un good example sull'uso di setTimeout che mantiene "questo".

simile al seguente:

function doSomething() { 
    // use 'this' to handle the selected element here 
} 

$(".someSelector").each(function() { 
    setTimeout(doSomething.bind(this), 0); 
}); 
3

Se si desidera utilizzare i parametri e regolare il numero massimo di funzioni asincrone è possibile utilizzare un semplice operaio async Sono accumulo:

var BackgroundWorker = function(maxTasks) { 
    this.maxTasks = maxTasks || 100; 
    this.runningTasks = 0; 
    this.taskQueue = []; 
}; 

/* runs an async task */ 
BackgroundWorker.prototype.runTask = function(task, delay, params) { 
    var self = this; 
    if(self.runningTasks >= self.maxTasks) { 
     self.taskQueue.push({ task: task, delay: delay, params: params}); 
    } else { 
     self.runningTasks += 1; 
     var runnable = function(params) { 
      try { 
       task(params); 
      } catch(err) { 
       console.log(err); 
      } 
      self.taskCompleted(); 
     } 
     // this approach uses current standards: 
     setTimeout(runnable, delay, params); 
    } 
} 

BackgroundWorker.prototype.taskCompleted = function() { 
    this.runningTasks -= 1; 

    // are any tasks waiting in queue? 
    if(this.taskQueue.length > 0) { 
     // it seems so! let's run it x) 
     var taskInfo = this.taskQueue.splice(0, 1)[0]; 
     this.runTask(taskInfo.task, taskInfo.delay, taskInfo.params); 
    } 
} 

Puoi utilizzarlo in questo modo:

var myFunction = function() { 
... 
} 
var myFunctionB = function() { 
... 
} 
var myParams = { name: "John" }; 

var bgworker = new BackgroundWorker(); 
bgworker.runTask(myFunction, 0, myParams); 
bgworker.runTask(myFunctionB, 0, null); 
8

Ecco una funzione che accetta un'altra funzione e output una versione che esegue async.

var async = function (func) { 
    return function() { 
    var args = arguments; 
    setTimeout(function() { 
     func.apply(this, args); 
    }, 0); 
    }; 
}; 

È usato come un modo semplice per effettuare una funzione asincrona:

var anyncFunction = async(function (callback) { 
    doSomething(); 
    callback(); 
}); 

Questo è differente dalla risposta di @ fider perché la funzione stessa ha la propria struttura (senza callback aggiunto il, esso è già nella funzione) e anche perché crea una nuova funzione che può essere utilizzata.

+0

setTimeout non può essere utilizzato in un ciclo * (chiama la stessa funzione più volte con argomenti distinti) *. – user2284570

+0

@ user2284570 Ecco a cosa servono le chiusure. '(funzione (a) {asyncFunction (a);}) (a)' – Swivel

+0

IIRC, è possibile ottenere questo risultato anche senza una chiusura: 'setTimeout (asyncFunction, 0, a);' – Swivel

3

Vedo qui che nessuno ha parlato della promessa. Quindi ti consiglio di informarti sulla promessa: see this link

1

Accanto all'ottima risposta di @pimvdb, e nel caso tu ti stia chiedendo, async.js non offre nemmeno funzioni veramente asincrone. Ecco una (molto) versione ridotta principale metodo della biblioteca:

function asyncify(func) { // signature: func(array) 
    return function (array, callback) { 
     var result; 
     try { 
      result = func.apply(this, array); 
     } catch (e) { 
      return callback(e); 
     } 
     /* code ommited in case func returns a promise */ 
     callback(null, result); 
    }; 
} 

Quindi la funzione protegge da errori e con grazia lo porge al callback per gestire, ma il codice è come sincrono come qualsiasi altra funzione JS.

1

Sfortunatamente, JavaScript non fornisce una funzionalità asincrona. Funziona solo in un singolo thread. Ma la maggior parte dei browser moderni fornisce Workers, che sono i secondi script che vengono eseguiti in background e possono restituire un risultato. Quindi, ho raggiunto una soluzione, penso che sia utile per eseguire in modo asincrono una funzione, che crea un worker per ogni chiamata asincrona.

Il codice seguente contiene la funzioneasyncper chiamare in background.

Function.prototype.async = function(callback) { 
    let blob = new Blob([ "self.addEventListener('message', function(e) { self.postMessage({ result: (" + this + ").apply(null, e.data) }); }, false);" ], { type: "text/javascript" }); 
    let worker = new Worker(window.URL.createObjectURL(blob)); 
    worker.addEventListener("message", function(e) { 
     this(e.data.result); 
    }.bind(callback), false); 
    return function() { 
     this.postMessage(Array.from(arguments)); 
    }.bind(worker); 
}; 

Questo è un esempio per l'utilizzo:

(function(x) { 
    for (let i = 0; i < 999999999; i++) {} 
     return x * 2; 
}).async(function(result) { 
    alert(result); 
})(10); 

Questa esegue una funzione che itera un for con un numero enorme di prendere tempo, come dimostrazione di asincronia, e quindi ottiene il doppio il numero passato. Il metodo async fornisce un function che chiama la funzione desiderata in background e in ciò che è fornito come parametro dei callback async lo return nel suo parametro univoco. Quindi nella funzione di richiamata I alert il risultato.

0

tardi, ma per mostrare una soluzione facile utilizzando promises dopo la loro introduzione in ES6, gestisce le chiamate asincrone molto più facile:

si imposta il codice asincrono in una nuova promessa:

var asyncFunct = new Promise(function(resolve, reject) { 
    $('#link').animate({ width: 200 }, 2000, function() { 
     console.log("finished");     
     resolve(); 
    });    
}); 

Nota per impostare resolve() al termine della chiamata asincrona.
Poi si aggiunge il codice che si desidera eseguire dopo la chiamata asincrona finiture all'interno .then() della promessa:

asyncFunct.then((result) => { 
    console.log("Exit");  
}); 

Ecco un frammento di esso:

$('#link').click(function() { 
 
    console.log("Enter"); 
 
    var asyncFunct = new Promise(function(resolve, reject) { 
 
     $('#link').animate({ width: 200 }, 2000, function() { 
 
      console.log("finished");    \t 
 
      resolve(); 
 
     }); \t \t \t 
 
    }); 
 
    asyncFunct.then((result) => { 
 
     console.log("Exit");  
 
    }); 
 
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<a href="#" id="link">Link</a> 
 
<span>Moving</span>

o JSFiddle

Problemi correlati