如何“等待”回调返回?


97

在使用简单的回调(例如以下示例)时:

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

如何更改功能以使用异步/等待?具体来说,假设“ someEvent”被保证只能被调用一次,我希望函数测试是一个异步函数,该异步函数在执行回调之前不会返回,例如:

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

1
仅供参考,ES7 / ES2016规范已最终确定,不包括async / await。目前,这只是第3阶段的提案
丹·普林斯

好吧,这令人惊讶-非常希望它能包括在内!感谢您提供信息@DanPrince
sean2078 '16

Answers:


142

async/await不是魔术。异步函数是可以为您解开Promises的函数,因此您需要api.on()返回一个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(),而是对值的承诺,可以这样使用:

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

3
返回承诺的函数的较短版本: const apiOn = (event) => new Promise(resolve => api.on(event, resolve));
Felipe Plets

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

异步/等待是魔术。您可以创建一个函数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');
}

args的数量是可变的。


1

您可以在不使用回调的情况下实现这一点,可以使用promise async await代替这里的回调。在这里,我还说明了两种处理错误的方法

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.