จะ "รอ" การติดต่อกลับได้อย่างไร?


100

เมื่อใช้การโทรกลับธรรมดาเช่นในตัวอย่างด้านล่าง:

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

ฟังก์ชันจะเปลี่ยนไปใช้ async / await ได้อย่างไร? โดยเฉพาะสมมติว่ามีการเรียกใช้ 'someEvent' ครั้งเดียวและครั้งเดียวฉันต้องการให้การทดสอบฟังก์ชันเป็นฟังก์ชัน async ซึ่งจะไม่ส่งคืนจนกว่าจะมีการเรียกกลับเช่น

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

1
สำหรับการอ้างอิงข้อกำหนด ES7 / ES2016 ได้รับการสรุปแล้วและไม่รวม async / await ในขณะที่มันเป็นเพียงข้อเสนอ 3 ขั้นตอน
Dan Prince

ที่น่าแปลกใจ - หวังเป็นอย่างยิ่งว่าจะได้รับการรวมเข้าด้วยกัน! ขอบคุณข้อมูล @DanPrince
sean2078

คำตอบ:


146

async/awaitไม่ใช่เวทมนตร์ ฟังก์ชัน async เป็นฟังก์ชันที่สามารถแกะคำสัญญาให้คุณได้ดังนั้นคุณจะต้อง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.
}

แต่นั่นก็เป็นเรื่องโกหกเช่นกันเพราะฟังก์ชัน async ยังส่งคืน Promises ด้วยตัวเองดังนั้นคุณจะไม่ได้รับค่าที่แท้จริงtest()แต่เป็น Promise for a value ซึ่งคุณสามารถใช้ได้ดังนี้:

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

3
เวอร์ชันที่สั้นกว่าสำหรับฟังก์ชันที่ส่งคืนคำสัญญา: const apiOn = (event) => new Promise(resolve => api.on(event, resolve));
Felipe Plets

7

มันน่ารำคาญที่ไม่มีวิธีแก้ปัญหาที่ตรงไปตรงมาและการห่อreturn new Promise(...)ก็น่ากลัว แต่ฉันพบวิธีแก้ปัญหาที่ใช้ได้util.promisify(จริงๆแล้วมันก็ห่อแบบเดียวกันด้วยนะดูดีกว่า)

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

ฟังก์ชันข้างต้นยังไม่ส่งคืนอะไรเลย เราสามารถทำให้มันคืนค่าPromiseของการresponseส่งผ่านcallbackโดยทำ:

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

จำนวน args เป็นตัวแปร


1

คุณสามารถทำได้โดยไม่ต้องโทรกลับใช้สัญญา async รอแทนการโทรกลับที่นี่ฉันจะทำอย่างไร และที่นี่ฉันได้แสดงสองวิธีในการจัดการข้อผิดพลาด

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.