如何“等待”回调返回?

How to "await" for a callback to return?

提问人:sean2078 提问时间:5/9/2016 最后编辑:Estus Flasksean2078 更新时间:4/3/2023 访问量:165967

问:

使用简单回调时,如下例所示:

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

如何将函数更改为使用 async / await?具体来说,假设“someEvent”保证被调用一次并且只调用一次,我希望函数测试是一个异步函数,在执行回调之前不会返回,例如:

async test() {
  return await api.on( 'someEvent' );
}
JavaScript 异步 回调 async-await ecmascript-2017

评论

1赞 Dan Prince 5/9/2016
仅供参考,ES7/ES2016 规范已经完成,其中不包括 async/await。目前,这只是一个第 3 阶段的提案
0赞 sean2078 5/9/2016
嗯,这很令人惊讶 - 非常希望它被包括在内!感谢您提供@DanPrince的信息

答:

231赞 Madara's Ghost 5/9/2016 #1

async/await不是魔术。异步函数是一个可以为您解包 Promise 的函数,因此您需要返回一个 Promise 才能正常工作。像这样的东西:api.on()

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.
}

但这也是一个谎言,因为异步函数本身也返回 Promise,所以你实际上不会从 中获取值,而是从一个值的 Promise 中获取,你可以像这样使用它:test()

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

评论

9赞 Felipe Plets 2/14/2019
返回 promise 的函数的较短版本:const apiOn = (event) => new Promise(resolve => api.on(event, resolve));
3赞 kekkles4 10/25/2022
我以为这很神奇
14赞 ErikD 9/13/2018 #2

很烦人,没有一个简单的解决方案,而且包装很模糊,但我找到了一个很好的解决方法(实际上它也做了同样的包装,只是看起来更好)。return new Promise(...)util.promisify

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

上面的函数还没有返回任何内容。我们可以通过执行以下操作使它返回传入的 a:Promiseresponsecallback

const util = require('util');

const asyncFunction = util.promisify(voidFunction);

现在我们实际上可以 .awaitcallback

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

使用时的一些规则util.promisify

  • 必须是函数的最后一个参数callbackpromisify
  • 假定的回调必须采用以下形式(err, res) => {...}

有趣的是,我们不需要专门写实际是什么。callback

评论

0赞 Luke 7/27/2022
需要注意的重要一点是,它位于 node js: nodejs.org/api/util.html#utilpromisifyoriginal 中,因此此解决方案在前端不起作用。'util'
3赞 noone 1/4/2019 #3

您可以在没有回调的情况下实现这一点,在这里使用 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)'/>

10赞 negstek 5/3/2019 #4

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

参数的数量是可变的。

5赞 sema toghem 6/4/2021 #5
const getprice = async () => {
return await new Promise((resolve, reject) => {
    binance.prices('NEOUSDT', (error, ticker) => {
        if (error) {
            reject(error)
        } else {
            resolve(ticker);
        }
    });
})}

router.get('/binanceapi/price', async function (req, res, next) {
res.send(await binanceAPI.getprice());});

评论

1赞 Pratap Alok Raj 6/4/2021
嗨@sema,欢迎来到 StackOverflow。请仔细阅读以下文档,了解如何写出一个好的答案,这将帮助您引起注意:- stackoverflow.com/help/how-to-answer
0赞 Petr Šimek 4/3/2023 #6

定义一个返回 promise 的函数:

export const callbackf = (f, ...args) => new Promise((resolve, reject) => {
    f(...args, (error, data) => {
        if (error) {
            reject(error);
        } else {
            resolve(data);
        }
    });
});

随时随地等待承诺。arg1、arg2、...是回调功能参数:

await callbackf(yourCallbackFunction, arg1, arg2, arg3, argN)