Làm thế nào để “chờ đợi” một cuộc gọi trở lại?


100

Khi sử dụng một lệnh gọi lại đơn giản, chẳng hạn như trong ví dụ dưới đây:

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

Làm cách nào để thay đổi chức năng để sử dụng async / await? Cụ thể, giả sử 'someEvent' được đảm bảo được gọi một lần và chỉ một lần, tôi muốn kiểm tra hàm là một hàm không đồng bộ sẽ không trả về cho đến khi lệnh gọi lại được thực thi chẳng hạn như:

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

1
Chỉ để tham khảo, thông số kỹ thuật ES7 / ES2016 đã được hoàn thiện và nó không bao gồm async / await. Hiện tại, nó chỉ là một đề xuất giai đoạn 3 .
Dan Prince

Điều đó thật đáng ngạc nhiên - Rất hy vọng nó được đưa vào! Cảm ơn vì thông tin @DanPrince
sean2078

Câu trả lời:


146

async/awaitkhông phải là ma thuật. Hàm async là một hàm có thể mở api.on()Promise cho bạn, vì vậy bạn sẽ cần trả về Promise để nó hoạt động. Một cái gì đó như thế này:

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

Sau đó

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

Nhưng đó cũng là một lời nói dối, bởi vì các hàm không đồng bộ cũng trả về chính các test()Promise , vì vậy bạn sẽ không thực sự nhận được giá trị mà thay vào đó, Promise cho một giá trị, bạn có thể sử dụng như vậy:

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

3
Một phiên bản ngắn hơn cho hàm trả về một lời hứa: const apiOn = (event) => new Promise(resolve => api.on(event, resolve));
Felipe Plets

7

Thật khó chịu khi không có một giải pháp đơn giản nào và việc bọc lại return new Promise(...)khá khó chịu, nhưng tôi đã tìm thấy một cách sử dụng khá ổn util.promisify(thực ra nó cũng thực hiện cách gói tương tự, chỉ trông đẹp hơn).

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

Hàm trên không trả về bất cứ thứ gì. Chúng ta có thể làm cho nó trả về một Promisetrong những giá trị responseđã chuyển callbackbằng cách:

const util = require('util');

const asyncFunction = util.promisify(voidFunction);

Bây giờ chúng ta có thể thực sự awaitcallback.

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

Một số quy tắc khi sử dụng util.promisify

  • Đối số callbackphải là đối số cuối cùng của hàm sẽ làpromisify
  • Cuộc gọi lại được cho là phải ở dạng (err, res) => {...}

Điều buồn cười là chúng ta không cần phải viết cụ thể callbackthực tế là gì.


3

async / await là ma thuật. Bạn có thể tạo một hàm asPromiseđể xử lý loại tình huống này:

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

và sau đó sử dụng nó khi bạn muốn:

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

số lượng args có thể thay đổi.


1

Bạn có thể đạt được điều này mà không cần gọi lại, sử dụng hứa không đồng bộ chờ đợi thay vì gọi lại ở đây, cách tôi sẽ làm điều này. Và cũng ở đây tôi đã minh họa hai phương pháp để xử lý lỗi

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.