153 votes

Comment créer une fonction asynchrone en Javascript ?

Regarde ça code :

<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");    
});

Comme vous pouvez le voir dans la console, la fonction "animate" est asynchrone, et elle "bifurque" le flux de code du bloc du gestionnaire d'événement. En effet :

$('#link').click(function () {
    console.log("Enter");
    asyncFunct();
    console.log("Exit");    
});

function asyncFunct() {
    console.log("finished");
}

suivez le flux du code-bloc !

Si je souhaite créer mon function asyncFunct() { } avec ce comportement, comment puis-je le faire avec javascript/jquery ? Je pense qu'il existe une stratégie sans l'utilisation de setTimeout()

0 votes

Jetez un coup d'œil aux sources de jQuery :)

0 votes

Le mathod .animate() utilise un callback. Animate appellera la callback lorsque l'animation sera terminée. Si vous souhaitez obtenir le même comportement que .animate(), vous avez besoin d'un callback (appelé par la fonction "main" après d'autres opérations). C'est différent si vous avez besoin d'une fonction asynchrone "complète" (une fonction appelée sans bloquer le flux d'exécution). Dans ce cas, vous pouvez utiliser setTimeout() avec un délai proche de 0.

0 votes

@Fabio Buda : pourquoi callback() devrait implémenter une sorte d'asynchrone ? En fait, ce n'est pas le cas jsfiddle.net/5H9XT/9

6voto

shanabus Points 6589

Cette page vous montre les bases de la création d'une fonction javascript asynchrone.

Depuis ES2017, les fonctions asynchrones de javacript sont beaucoup plus faciles à écrire. Vous devriez également lire davantage sur Promesses .

0 votes

Le lien est mort.

6voto

hd84335 Points 1820

En retard, mais pour montrer une solution facile utilisant promises après leur introduction dans ES6 il gère les appels asynchrones beaucoup plus facilement :

Vous placez le code asynchrone dans une nouvelle promesse :

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

Note à l'attention de l'utilisateur resolve() lorsque l'appel asynchrone se termine.
Ensuite, vous ajoutez le code que vous voulez exécuter après la fin de l'appel asynchrone à l'intérieur de .then() de la promesse :

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

En voici un extrait :

$('#link').click(function () {
    console.log("Enter");
    var asyncFunct = new Promise(function(resolve, reject) {
        $('#link').animate({ width: 200 }, 2000, function() {
            console.log("finished");                
            resolve();
        });             
    });
    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>

ou JSFiddle

0 votes

D'après ce que je comprends, le code dans l'exécuteur (l'argument à new Promise) est exécuté immédiatement, et non au prochain tick. Je ne suis donc pas sûr que cette réponse soit correcte. Cependant, il semble que le gestionnaire then soit toujours exécuté dans un tick ultérieur.

3voto

João Alves Points 121

Si vous voulez utiliser des paramètres et réguler le nombre maximum de fonctions asynchrones, vous pouvez utiliser un simple worker asynchrone que j'ai construit :

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);
    }
}

Vous pouvez l'utiliser comme ceci :

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

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

2voto

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);

Bien qu'elle ne soit pas fondamentalement différente des autres solutions, je pense que ma solution apporte quelques avantages supplémentaires :

  • il permet d'ajouter des paramètres aux fonctions
  • il passe la sortie de la fonction à la callback
  • il est ajouté à Function.prototype permettant une façon plus agréable de l'appeler

De même, la similitude avec la fonction intégrée Function.prototype.apply me semble approprié.

1voto

Mike M Points 932

Suite à l'excellente réponse de @pimvdb, et juste au cas où vous vous poseriez la question, async.js ne propose pas non plus de fonctions véritablement asynchrones. Voici une version (très) simplifiée de la méthode principale de la bibliothèque :

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);
    };
}

Ainsi, la fonction protège des erreurs et les transmet gracieusement à la fonction de rappel pour qu'elle les gère, mais le code est aussi synchrone que n'importe quelle autre fonction JS.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X