Javascript 如何“等待”回调返回?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37104199/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 19:55:21  来源:igfitidea点击:

How to "await" for a callback to return?

javascriptasynchronouscallbackasync-awaitecmascript-2017

提问by sean2078

When using a simple callback such as in the example below:

使用简单回调时,例如在下面的示例中:

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

How can the function be changed to use async / await? Specifically, assuming 'someEvent' is guaranteed to be called once and only once, I'd like the function test to be an async function which does not return until the callback is executed such as:

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

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

回答by Madara's Ghost

async/awaitis not magic. An async function is a function that can unwrap Promises for you, so you'll need api.on()to return a Promise for that to work. Something like this:

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

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

Then

然后

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


But that's a lie too, because async functions also return Promises themselves, so you aren't going to actually get the value out of test(), but rather, a Promise for a value, which you can use like so:

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

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

回答by ErikD

It's annoying that there isn't a straightforward solution, and wrapping return new Promise(...)is fugly, but I have found an ok work-around using util.promisify(actually it also kinda does the same wrapping, just looks nicer).

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

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

The above function does not return anything, yet. We can make it return a Promiseof the responsepassed in callbackby doing:

上面的函数还没有返回任何东西。我们可以把它返回Promiseresponse传入callback做:

const util = require('util');

const asyncFunction = util.promisify(voidFunction);

Now we can actually awaitthe callback.

现在我们实际上可以awaitcallback.

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

Some rules when using util.promisify

使用时的一些规则 util.promisify

  • The callbackmust be the last argument of the function that is gonna be promisify
  • The supposed-callback must be in the form (err, res) => {...}
  • callback必须是函数的最后一个参数是要去promisify
  • 假定的回调必须采用以下形式 (err, res) => {...}

Funny thing is we do not need to ever specifically write what's the callbackactually is.

有趣的是,我们不需要特别写出callback实际情况。

回答by NuOne

You can achieve this without callbacks , use promise async await instead of callbacks here how I would do this. And also here I have illustrated two methods to handle errors

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

async/await is magic. You can create a function asPromiseto handle this kind of situations:

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

and then use it when you want:

然后在需要时使用它:

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

the number of args is variable.

args 的数量是可变的。