web-dev-qa-db-fra.com

Comment "attendre" un rappel pour revenir?

Lors de l'utilisation d'un rappel simple comme dans l'exemple ci-dessous:

test() {
  api.on( 'someEvent', function( response ) {
    return response;
  });
}

Comment la fonction peut-elle être modifiée pour utiliser asynchrone/wait? Spécifiquement, en supposant que «someEvent» soit garanti d'être appelé une fois et une fois seulement, j'aimerais que le test de fonction soit une fonction asynchrone qui ne retourne pas tant que le rappel n'est pas exécuté, tel que

async test() {
  return await api.on( 'someEvent' );
}
52
sean2078

async/await n'est pas magique. Une fonction asynchrone est une fonction qui peut décompresser les promesses pour vous, vous aurez donc besoin de api.on() pour renvoyer une promesse pour que cela fonctionne. Quelque chose comme ça:

function apiOn(event) {
  return new Promise(resolve => {
    api.on(event, response => resolve(response));
  });
}

Ensuite

async function test() {
  return await apiOn( 'someEvent' ); // await is actually optional here
                                      // you'd return a Promise either way.
}

Mais c’est aussi un mensonge, car les fonctions asynchrones renvoient aussi des promesses elles-mêmes. Ainsi, vous n’obtiendrez pas la valeur de test(), mais plutôt une promesse pour une valeur que vous pouvez utiliser comme suit:

async function whatever() {
  // snip
  const response = await test();
  // use response here
  // snip
}
73
Madara Uchiha

Il est agaçant qu’il n’existe pas de solution simple et que l’emballage de return new Promise(...) soit lamentable, mais j’ai trouvé une solution de contournement correcte en utilisant util.promisify (en fait, il effectue également un peu l’emballage identique, mais en est plus joli).

function voidFunction(someArgs, callback) {
  api.onActionwhichTakesTime(someMoreArgs, (response_we_need) => {
    callback(null, response_we_need);
  });
}

La fonction ci-dessus ne renvoie rien pour l'instant. Nous pouvons lui faire renvoyer une Promise de la response passée dans callback en faisant:

const util = require('util');

const asyncFunction = util.promisify(voidFunction);

Maintenant, nous pouvons réellement await la callback.

async function test() {
  return await asyncFunction(args);
}

Quelques règles lors de l'utilisation de util.promisify

  • La callback doit être le dernier argument de la fonction qui va être promisify
  • Le supposé rappel doit être de la forme (err, res) => {...}

Ce qui est drôle, c’est que nous n’avons pas besoin d’écrire précisément en quoi consiste la callback

3
ErikD

Vous pouvez y parvenir sans rappel, utilisez promesse async attendre au lieu de rappel ici. Et aussi ici, j'ai illustré deux méthodes pour gérer les erreurs 

clickMe = async (value) => {
  
  // begin to wait till the message gets here;
  let {message, error} = await getMessage(value);
  
  // if error is not null
  if(error)
    return console.log('error occured ' + error);
   
  return console.log('message ' + message);

}

getMessage = (value) => {

  //returning a promise 
  return new Promise((resolve, reject) => {
  
    setTimeout(() => {
      // if passed value is 1 then it is a success
      if(value == 1){
        resolve({message: "**success**", error: null});
      }else if (value == 2){
        resolve({message: null, error: "**error**"});
      }
    }, 1000);
  
  });

}

clickWithTryCatch = async (value) => {

  try{
    //since promise reject in getMessage2 
    let message = await getMessage2(value);
    console.log('message is ' + message);
  }catch(e){
    //catching rejects from the promise
    console.log('error captured ' + e);
  }

}

getMessage2 = (value) => {

  return new Promise((resolve, reject) => {
  
    setTimeout(() => {
      if(value == 1)
        resolve('**success**');
      else if(value == 2)
        reject('**error**'); 
    }, 1000);
  
  });

}
<input type='button' value='click to trigger for a value' onclick='clickMe(1)' />
<br/>
<input type='button' value='click to trigger an error' onclick='clickMe(2)' />
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(1)'/>
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(2)'/>

1
NuOne T Attygalle

async/wait est magique. Vous pouvez créer une fonction asPromise pour gérer ce type de situation:

function asPromise(context, callbackFunction, ...args) {
    return new Promise((resolve, reject) => {
        args.Push((err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
        if (context) {
            callbackFunction.call(context, ...args);
        } else {
            callbackFunction(...args);
        }
    });
}

et ensuite l'utiliser quand vous voulez:

async test() {
  return await this.asPromise(this,api.on,'someEvent' );
}

args nunmber sont des dynamiques.

0
negstek