如何“等待”回调?

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

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

如何将函数更改为使用异步/等待?具体来说,假设‘ some Event’被保证调用一次且仅被调用一次,我希望函数 test 是一个异步函数,在执行回调之前不会返回,比如:

async test() {
return await api.on( 'someEvent' );
}
145780 次浏览

async/await不是魔法。一个异步函数是一个可以为你展开承诺的函数,所以你需要 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.
}

但是这也是一个谎言,因为异步函数本身也返回  承诺,所以实际上你不会从 test()中得到值,而是从一个值中得到一个承诺,你可以这样使用:

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

令人恼火的是没有一个直接的解决方案,而且包装 return new Promise(...)很难看,但是我已经找到了一个使用 util.promisify的解决方案(实际上它也可以做同样的包装,只是看起来更好)。

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

上面的函数还没有返回任何内容。我们可以让它返回在 callback中传递的 responsePromise,方法是:

const util = require('util');


const asyncFunction = util.promisify(voidFunction);

现在我们可以实际上 awaitcallback

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

使用 util.promisify的一些规则

  • callback必须是将为 promisify的函数的最后一个参数
  • 假定的回调必须采用 (err, res) => {...}格式

有趣的是,我们并不需要明确地写出 callback到底是什么。

您可以在没有回调的情况下实现这一点,在这里我将如何做到这一点,使用异步承诺等待而不是回调。这里我还举例说明了两种处理错误的方法

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

您可以创建一个函数 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');
}

参数的数目是可变的。

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