コールバックが戻るのを「待つ」方法は?


102

以下の例のような単純なコールバックを使用する場合:

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

async / awaitを使用するように関数を変更するにはどうすればよいですか?具体的には、「someEvent」が1回だけ呼び出されることが保証されていると仮定して、関数testを、次のようにコールバックが実行されるまで返されない非同期関数にしたいと思います。

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

1
参考までに、ES7 / ES2016仕様は最終決定されており、async / awaitは含まれていません。現時点では、ステージ3の提案にすぎません。
ダンプリンス

それは驚くべきことです-それが含まれることを非常に望んでいます!情報をありがとう@ DanPrince– sean2078 2016
20:23

回答:


150

async/await魔法ではありません。非同期関数はapi.on()Promiseをアンラップできる関数なので、それを機能させるにはPromiseを返す必要があります。このようなもの:

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

次に

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

しかし、それも嘘です。非同期関数もPromises自体を返すため、実際に値を取得するのでtest()はなく、次のように使用できる値のPromiseを取得します。

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

3
promiseを返す関数の短いバージョン: const apiOn = (event) => new Promise(resolve => api.on(event, resolve));
FelipePlets19年

7

簡単な解決策がないのreturn new Promise(...)は面倒で、ラッピングはあいまいですが、使用して問題のない回避策を見つけましたutil.promisify(実際には、同じラッピングを実行しますが、見た目が良くなります)。

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

上記の関数はまだ何も返しません。次のようにしてPromiseresponse渡されたのを返すようにすることができますcallback

const util = require('util');

const asyncFunction = util.promisify(voidFunction);

これで、実際awaitcallback

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

使用する際のいくつかのルール util.promisify

  • callback可能つもりで、関数の最後の引数でなければなりませんpromisify
  • 想定されるコールバックは、次の形式である必要があります (err, res) => {...}

面白いことに、callback実際に何であるかを具体的に書く必要はありません。


3

async / awaitは魔法です。asPromiseこのような状況を処理する関数を作成できます。

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

そして、必要なときにそれを使用します。

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

引数の数は可変です。


1

コールバックなしでこれを達成できます。ここでは、コールバックの代わりにpromise asyncawaitを使用します。また、ここでは、エラーを処理する2つの方法を示しました。

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)'/>

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.