Apakah itu anti-pola untuk menggunakan async / await di dalam konstruktor Promise () baru?


93

Saya menggunakan async.eachLimitfungsi untuk mengontrol jumlah operasi maksimum dalam satu waktu.

const { eachLimit } = require("async");

function myFunction() {
 return new Promise(async (resolve, reject) => {
   eachLimit((await getAsyncArray), 500, (item, callback) => {
     // do other things that use native promises.
   }, (error) => {
     if (error) return reject(error);
     // resolve here passing the next value.
   });
 });
}

Seperti yang Anda lihat, saya tidak bisa mendeklarasikan myFunctionfungsi sebagai async karena saya tidak memiliki akses ke nilai di dalam callback kedua dari eachLimitfungsi tersebut.


"Seperti yang Anda lihat, saya tidak dapat mendeklarasikan myFungsi sebagai asinkron" --- dapatkah Anda menjelaskan lebih lanjut?
zerkms

1
Oh, oke ... maaf. Saya memerlukan konstruktor karena saya memerlukan async.eachLimit untuk menghindari lebih dari 500 operasi asinkron pada satu waktu. Saya mengunduh dan mengekstrak data dari file teks dan saya ingin menghindari banyak operasi asinkron, setelah saya mengekstrak data, saya harus mengembalikan Janji dengan data, dan saya tidak akan dapat mengembalikannya dari callback async.eachLimit .

1. Mengapa Anda perlu menunggu? Asinkron sudah menjadi mekanisme aliran kontrol. 2. Jika Anda ingin menggunakan async.js dengan promise di dalam node.js, lihat async-q
slebetman

Untuk menghindari panggilan balik neraka, dan jika sesuatu melempar, janji luar akan menangkap.

Jawaban:


81

Anda secara efektif menggunakan promise di dalam fungsi pelaksana konstruktor promise, jadi ini adalah anti-pola konstruktor Promise .

Kode Anda adalah contoh bagus dari risiko utama: tidak menyebarkan semua kesalahan dengan aman. Baca alasannya di sana .

Selain itu, penggunaan async/ awaitdapat membuat perangkap yang sama menjadi lebih mengejutkan. Membandingkan:

let p = new Promise(resolve => {
  ""(); // TypeError
  resolve();
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e)); // Catches it.

dengan asyncpadanan yang naif (salah) :

let p = new Promise(async resolve => {
  ""(); // TypeError
  resolve();
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e)); // Doesn't catch it!

Lihat di konsol web browser Anda untuk yang terakhir.

Yang pertama berfungsi karena setiap pengecualian langsung dalam fungsi pelaksana konstruktor Promise dengan mudah menolak janji yang baru dibuat (tetapi di dalam apa pun .thenAnda sendiri).

Yang kedua tidak berfungsi karena ada pengecualian langsung dalam suatu asyncfungsi menolak janji implisit yang dikembalikan oleh asyncfungsi itu sendiri .

Karena nilai hasil dari fungsi pelaksana konstruktor promise tidak digunakan, itu berita buruk!

Kode Anda

Tidak ada alasan Anda tidak dapat mendefinisikan myFunctionsebagai async:

async function myFunction() {
  let array = await getAsyncArray();
  return new Promise((resolve, reject) => {
    eachLimit(array, 500, (item, callback) => {
      // do other things that use native promises.
    }, error => {
      if (error) return reject(error);
      // resolve here passing the next value.
    });
  });
}

Meskipun mengapa menggunakan pustaka kontrol konkurensi yang sudah ketinggalan zaman await?


12
Anda tidak perlu return await: return new Promisecukup.
lonesomeday

2
Saya secara resmi menyetujui jawaban ini, saya akan mengatakan hal yang persis sama :-)
Bergi

1
@celoxxx Lihat di sini . Anda memang tidak boleh menggunakan async.js dengan janji
Bergi

1
@celoxxx Jatuhkan jenis dan itu menjadi js biasa. Anda sebaiknya tidak menggunakan async.js karena antarmuka yang berbeda - panggilan balik bergaya node vs janji - menyebabkan terlalu banyak gesekan dan menyebabkan kode rumit dan rawan kesalahan yang tidak perlu.
Bergi

1
Saya setuju dengan Anda ... Tapi kode ini sudah lama, dan saya refactoring untuk menggunakan event + async.js (untuk mengontrol batas async, belum. Jika Anda tahu cara yang lebih baik, tolong katakan).

16

Saya setuju dengan jawaban yang diberikan di atas dan tetap saja, terkadang lebih baik untuk memiliki asinkron di dalam janji Anda, terutama jika Anda ingin merangkai beberapa operasi yang mengembalikan janji dan menghindari then().then()neraka. Saya akan mempertimbangkan untuk menggunakan sesuatu seperti ini dalam situasi itu:

const operation1 = Promise.resolve(5)
const operation2 = Promise.resolve(15)
const publishResult = () => Promise.reject(`Can't publish`)

let p = new Promise((resolve, reject) => {
  (async () => {
    try {
      const op1 = await operation1;
      const op2 = await operation2;

      if (op2 == null) {
         throw new Error('Validation error');
      }

      const res = op1 + op2;
      const result = await publishResult(res);
      resolve(result)
    } catch (err) {
      reject(err)
    }
  })()
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e));
  1. Fungsi yang diteruskan ke Promisekonstruktor tidak asinkron, jadi linter tidak menampilkan kesalahan.
  2. Semua fungsi asinkron dapat dipanggil secara berurutan menggunakan await.
  3. Kesalahan khusus dapat ditambahkan untuk memvalidasi hasil operasi asinkron
  4. Kesalahan akhirnya tertangkap dengan baik.

Kekurangannya adalah Anda harus ingat meletakkan try/catchdan memasangnya reject.


4
static getPosts(){
    return new Promise( (resolve, reject) =>{
        try {
            const res =  axios.get(url);
            const data = res.data;
            resolve(
                data.map(post => ({
                    ...post,
                    createdAt: new Date(post.createdAt)
                }))
            )
        } catch (err) {
            reject(err);                
        }
    })
}

hapus await dan async akan menyelesaikan masalah ini. karena kamu sudah mengaplikasikan objek Promise, itu sudah cukup.


Jadi dalam contoh Anda, akan axios.get(url)berfungsi seolah-olah disebut sebagai await axios.get(url)?
PrestonDocks
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.