javascript - Apakah mungkin membuat fungsi async mengembalikan Query mongoose alih-alih Promise?


Pengaturan

Misalkan kita memiliki beberapa dokumen Foo dan Bar. Foo memiliki bidang name yang dapat digunakan untuk mengidentifikasi dokumen selain menggunakan _id dokumen. Bar berisi referensi ke dokumen Foo.

const fooSchema = new mongoose.Schema({
  name: {
    type: String,
    unique: true
  }
});
const Foo = <any>mongoose.model<any>('Foo', fooSchema);

const barSchema = new mongoose.Schema({
  foo: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Foo'
  },
  date: Date
});
const Bar = <any>mongoose.model<any>('Bar', barSchema);

Sekarang misalkan kita memiliki name dari dokumen Foo, tetapi bukan _id nya. Kita ingin menemukan semua dokumen Bar yang merujuk pada dokumen Foo tersebut. Jadi kita menulis sebuah fungsi yang pertama-tama menemukan dokumen Foo dengan name yang diberikan, kemudian menggunakannya untuk menemukan dokumen Bar.

barSchema.statics.findBarWithFooName = async function(name: string) {
  let foo = await Foo.findOne({ name: name });
  return Bar.find({ foo: foo });
};

Dan kita bisa menggunakan fungsi ini dengan menulis:

let bars = await Bar.findBarWithFooName('fooName');

Masalah

Namun, kita juga ingin mengurutkan bars berdasarkan tanggal. Biasanya, dengan pembangun Query mongoose, kita bisa menulis sesuatu seperti ini:

let bars = await Bar.find({})
                    .sort('date');

Namun, jika kita malah menggunakan fungsi yang kita tulis:

let bars = await Bar.findBarWithFooName('fooName')
                    .sort('date');

Sebaliknya, kita akan mendapatkan sebuah kesalahan:

UnhandledPromiseRejectionWarning: Penolakan janji yang tidak ditangani (id penolakan: 1): TypeError: Bar.findBarWithFooName(...).sort bukan fungsi

Debugging

Saat menyelidiki dengan console.log(bars), saya menemukan bahwa ketika kita memanggil Bar.find({}), objek yang dihasilkan adalah objek Query dari mongoose.

Namun, ketika kita memanggil Bar.findBarWithFooName('fooName'), objek yang dihasilkan adalah Promise { <pending> }. Jadi masuk akal bahwa metode sort tidak ada pada Promise { <pending> }.

Pertanyaan

Pertanyaan saya kemudian, mengapa dalam kasus pertama, itu mengembalikan Query, tetapi dalam kasus kedua, itu mengembalikan Promise { <pending> }? Dan apa yang bisa saya lakukan agar findBarWithFooName mengembalikan Query sehingga saya bisa melanjutkan untuk membangun query, alih-alih Promise { <pending> }?

Saya menyadari bahwa salah satu solusi yang bisa saya gunakan adalah dengan mengambil await Foo.findOne({ name: name }) dari findBarWithFooName, dan cukup mengambil _id sebagai parameter daripada name. Namun, saya mencoba untuk menyembunyikan proses harus mencari Foo terlebih dahulu, itulah sebabnya saya mencoba membuat satu fungsi yang mengembalikan Query saja.

Jawaban #1

Tidak

Tidak mungkin membuat async function mengembalikan objek kustom alih-alih Promise - bahkan bukan subclass dari promise pun.

Tidak

Anda tidak bisa membuat findBarWithFooName Anda mengembalikan query bagaimanapun, karena harus menunggu Foo.findOne sebelum Bar.find bahkan dipanggil dan membangun objek Query yang Anda cari.

Untuk menyelesaikan masalah Anda, Anda perlu mengembalikan beberapa pembungkus untuk kueri - Anda tidak dapat return kueri secara langsung karena itu bisa dijanjikan dan akan secara implisit ditunggu. Pemanggilan kemudian akan terlihat seperti

let bars = await ( unwrap(await Bar.findBarWithFooName('fooName')).sort('date') );

dengan unwrap menjadi sesuatu yang mengambil Query dari pembungkusnya. Sebagai pembungkus, Anda dapat menggunakan apa saja tanpa metode then - bisa berupa closure, objek dengan properti, array dengan satu elemen…