【问题标题】:How to "await" for a callback to return?如何“等待”回调返回?
【发布时间】:2016-09-03 09:43:43
【问题描述】:

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

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

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

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

【问题讨论】:

  • 仅供参考,ES7/ES2016规范已经定稿,不包含async/await。目前它只是一个stage 3 proposal
  • 嗯,这很令人惊讶 - 非常希望它被包括在内!感谢@DanPrince 提供的信息

标签: javascript asynchronous callback async-await ecmascript-2017


【解决方案1】:

async/await 不是魔法。异步函数是一个可以为您解包 Promise 的函数,因此您需要 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() 中获取值,而是一个值的 Promise,你可以像这样使用它:

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

【讨论】:

  • 返回 promise 的函数的较短版本:const apiOn = (event) => new Promise(resolve => api.on(event, resolve));
【解决方案2】:

令人讨厌的是没有一个简单的解决方案,并且包装 return new Promise(...) 是丑陋的,但我发现使用 util.promisify 的解决方法很好(实际上它也有点做同样的包装,只是看起来更好)。

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

上面的函数还没有返回任何东西。我们可以让它返回 response 中的 Promise,方法是这样做:

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

    参数的数量是可变的。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案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());});
        

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-15
        • 1970-01-01
        • 2018-09-27
        • 2019-02-11
        • 2011-01-06
        相关资源
        最近更新 更多