Bagaimana cara "menunggu" callback untuk kembali?

100

Saat menggunakan callback sederhana seperti pada contoh di bawah ini:

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

Bagaimana fungsi diubah untuk menggunakan async / await? Secara khusus, dengan asumsi 'someEvent' dijamin akan dipanggil sekali dan hanya sekali, saya ingin pengujian fungsi menjadi fungsi asinkron yang tidak kembali sampai callback dijalankan seperti:

async test() {
  return await api.on( 'someEvent' );
}
sean2078
sumber
1
Sekadar referensi, spesifikasi ES7 / ES2016 telah difinalisasi dan tidak menyertakan async / await. Saat ini itu hanya proposal tahap 3 .
Dan Prince
Nah, itu mengejutkan - Sangat berharap itu disertakan! Terima kasih atas infonya @DanPrince
sean2078

Jawaban:

146

async/awaitbukan sihir. Fungsi asinkron adalah fungsi yang dapat membuka bungkusan Promises untuk Anda, jadi Anda memerlukannyaapi.on() mengembalikan Promise agar dapat berfungsi. Sesuatu seperti ini:

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

Kemudian

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

Tapi itu juga bohong, karena fungsi async juga mengembalikan Promises sendiri, jadi Anda tidak akan benar-benar mendapatkan nilainya test(), melainkan Promise untuk sebuah nilai, yang dapat Anda gunakan seperti ini:

async function whatever() {
  // snip
  const response = await test();
  // use response here
  // snip
}
Hantu Madara
sumber
3
Versi yang lebih pendek untuk fungsi yang mengembalikan sebuah janji: const apiOn = (event) => new Promise(resolve => api.on(event, resolve));
Felipe Plets
7

Sangat menjengkelkan bahwa tidak ada solusi yang langsung, dan pembungkusan return new Promise(...)sangat buruk, tetapi saya telah menemukan solusi yang baik untuk digunakan util.promisify(sebenarnya itu juga melakukan pembungkusan yang sama, hanya terlihat lebih bagus).

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

Fungsi di atas belum mengembalikan apa pun. Kita bisa membuatnya mengembalikan Promisedari responseberlalu dalam callbackdengan melakukan:

const util = require('util');

const asyncFunction = util.promisify(voidFunction);

Sekarang kita dapat benar-benar awaityang callback.

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

Beberapa aturan saat menggunakan util.promisify

  • Itu callback harus argumen terakhir dari fungsi yang akan menjadipromisify
  • Panggilan balik yang seharusnya harus dalam bentuk (err, res) => {...}

Lucunya, kita tidak perlu pernah secara spesifik menulis apa yang callbacksebenarnya.

ErikD
sumber
3

async / await adalah keajaiban. Anda dapat membuat fungsi asPromiseuntuk menangani situasi seperti ini:

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

lalu gunakan saat Anda ingin:

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

jumlah arg adalah variabel.

negstek
sumber
1

Anda dapat mencapai ini tanpa callback, gunakan promise async await daripada callback di sini, seperti yang saya lakukan. Dan juga di sini saya telah mengilustrasikan dua metode untuk menangani kesalahan

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

NuOne
sumber