Async: Perlakukan fungsi tanpa panggilan balik sebagai janji

Dibuat pada 9 Jul 2019  ·  12Komentar  ·  Sumber: caolan/async

Perilaku saat ini sangat mengejutkan:

await async.parallel({foo: async()=>Promise.all([1,2])})
// { foo: [ 1, 2 ] }
await async.parallel({foo: ()=>Promise.all([1,2])})
// waits forever because non-async function expects a ballback

Karena untuk fungsi yang pendek, tergoda untuk menulis foo: () => doSomething() . Sangat mudah untuk melupakan kata kunci async sana

Tetapi saat ini ini tidak akan berfungsi, itu perlu ditulis sebagai foo: async () => doSomething()

Bisakah kita memeriksa apakah panggilan balik diteruskan atau tidak, daripada memeriksa apakah fungsinya adalah AsyncFunction?

wont fix

Komentar yang paling membantu

Bahkan dengan masalah ini ditutup, pada masalah tertaut di atas, saya ingin menekankan kembali pada pernyataan @caub di sini.
Perilaku saat ini sangat membingungkan saya, dan sementara saya (semacam) memahami batasan teknis yang dibahas di sini, saya percaya bahwa dokumentasi tidak sejalan dengan perilaku saat ini.
Misalnya mapLimit dikatakan "Pengembalian: janji, jika tidak ada panggilan balik yang diteruskan" - Saya mulai dengan versi panggilan balik, dan kemudian pada dasarnya mengabaikan panggilan balik dan terkejut bahwa saya tidak mendapatkan Janji (karena saya tidak punya kata kunci 'async').
juga, pernyataan 'panggilan balik' dalam fungsi iteratee tidak diperlukan saat itu, sebaliknya Anda perlu 'mengembalikan' nilai.
Jadi, perilaku harus diubah (lebih disukai) atau setidaknya dokumentasi dan contoh harus diselaraskan dalam sudut pandang saya untuk menghindari kebingungan.

Semua 12 komentar

Kami tidak memiliki cara untuk dapat mendeteksi dengan andal apakah suatu fungsi mengembalikan janji atau tidak, dan apakah kami perlu meneruskan panggilan balik ke sana atau tidak sebelumnya. ( Function.length atau parsing Function.toString() tidak cukup baik) Ini tentang asyncify -- mengonversi fungsi yang mengembalikan janji ke fungsi yang menerima panggilan balik. Karena kita dapat mendeteksi fungsi async karena memiliki tipe yang berbeda, kita dapat secara otomatis asyncify mereka (begitulah cara kerjanya secara internal). Tetapi untuk fungsi biasa yang mengembalikan janji, kami tidak memiliki cara untuk mengetahuinya sebelum Async memanggilnya.

@aearly hmm benar, jadi mungkin sesuatu yang lebih sederhana seperti mendeteksi panggilan balik di tingkat atas?

async function parallel(o, cb) {
  if (!cb) { // promise behavior
    return Object.fromEntries(
      await Promise.all(Object.entries(o).map(async ([k, v]) => [k, await v()]))
    )
  }
  // else do the callback behavior
}

Kami sudah melakukan sesuatu seperti itu -- kembalikan janji jika Anda menghilangkan panggilan balik ke async.parallel . Kami tidak menganggap fungsi yang Anda berikan padanya mengembalikan janji jika Anda menghilangkan panggilan balik karena kami tidak memiliki cara untuk mengasumsikan maksud pengguna. Mungkin saya salah paham dengan maksud Anda?

Oke, mari kita konkrit: https://repl.it/@caub/async -async

// npm.im/async expects all inner functions to be async,
// even when using no outer callback (as 2nd arg of async.parallel)
console.log(
  await async.parallel({
    foo: async () => delay(1000).then(() => 1),
    bar: async (rs) => {
      return 2
    },
  })
);

// Doesn't work without those async,
// but I'd like this to resolve just like above instead of freezing
console.log(
  await async.parallel({
    foo: () => delay(1000).then(() => 1),
    bar: () => {
      return 2
    },
  })
);

Saya pikir, di versi utama berikutnya di masa mendatang, mungkin menarik untuk hanya mendukung panggilan balik dengan cara yang mirip dengan ini:

await Promise.all([
  { then(resolve){ setTimeout(resolve, 50, 1) } }, // callback way
  2,
  delay(45).then(())=>3)
])
// [1, 2, 3]

menghilangkan kebutuhan kata kunci async di fungsi dalam

Keuntungannya adalah lebih jelas. Saya mengerti maksud Anda tentang tidak mengasumsikan niat pengguna, tetapi Anda masih berasumsi orang tidak akan melupakan kata kunci async ketika ingin menggunakan penyelesaian Janji

Itu akan merusak kompatibilitas mundur dengan cara yang cukup besar -- misalnya Anda tidak dapat melakukan hal-hal seperti Async.map(fileNames, fs.readFile) lagi.

Bisakah Anda menjelaskan di mana itu akan pecah?

Yang saat ini istirahat adalah:

await async.map(['server.js', 'package.json'], fs.readFile) // works
await async.map(['server.js', 'package.json'], fs.promises.readFile) // works
await async.map(['server.js', 'package.json'], s => fs.promises.readFile(s)) // doesn't work
await async.map(['server.js', 'package.json'], async s => fs.promises.readFile(s)) // works

Idenya adalah untuk memperbaiki yang ke-3

Saya mendapatkan masalah itu terus-menerus, misalnya saya menulis:

  // ...
  doc = await async.map(items, item => item.toDoc(currentUser, options));

di suatu tempat, dan sekali lagi, waktu habis karena saya kehilangan async item => item.toDoc..

Jika saya sendirian dalam kasus itu, itu tidak layak, tetapi jika lebih banyak orang berada dalam situasi ini, saya pikir itu layak untuk mempertimbangkan perubahan yang diusulkan.

Contoh dengan async.map ini juga sangat representatif, karena Anda dapat melihat maksud saya dengan caranya

  doc = await Promise.all(items.map(item => item.toDoc(currentUser, options)));

bekerja tanpa perlu async item => item.toDoc..

Tidak ada cara untuk memperbaiki yang ke-3 dengan andal, ini telah banyak dibahas di masa lalu. Kami tidak dapat memeriksa nilai kembalian suatu fungsi dan kemudian secara surut tidak meneruskan panggilan balik ke fungsi tersebut. Kata kunci async sangat bagus.

@caub Pasang tak tahu malu di sini, tapi saya pikir asyncp memiliki kemampuan untuk menangani apa yang Anda cari di sini.

Bahkan dengan masalah ini ditutup, pada masalah tertaut di atas, saya ingin menekankan kembali pada pernyataan @caub di sini.
Perilaku saat ini sangat membingungkan saya, dan sementara saya (semacam) memahami batasan teknis yang dibahas di sini, saya percaya bahwa dokumentasi tidak sejalan dengan perilaku saat ini.
Misalnya mapLimit dikatakan "Pengembalian: janji, jika tidak ada panggilan balik yang diteruskan" - Saya mulai dengan versi panggilan balik, dan kemudian pada dasarnya mengabaikan panggilan balik dan terkejut bahwa saya tidak mendapatkan Janji (karena saya tidak punya kata kunci 'async').
juga, pernyataan 'panggilan balik' dalam fungsi iteratee tidak diperlukan saat itu, sebaliknya Anda perlu 'mengembalikan' nilai.
Jadi, perilaku harus diubah (lebih disukai) atau setidaknya dokumentasi dan contoh harus diselaraskan dalam sudut pandang saya untuk menghindari kebingungan.

@caub Inilah alasannya
https://github.com/caolan/async/blob/8aecf108b3922bc5211036706a0f6f75e02bd42b/lib/internal/wrapAsync.js#L3 :L5

function isAsync(fn) {
    return fn[Symbol.toStringTag] === 'AsyncFunction';
}

Dengan cara ini, lebih mudah untuk memeriksa apakah fungsi mengembalikan janji tanpa benar-benar memanggilnya. Tetapi tetap setuju dengan Anda bahwa ini bertentangan dengan konvensi async/menunggu. Karena menambahkan kata kunci async tanpa await internal sebenarnya dianggap sebagai praktik yang buruk di sebagian besar kasus.

dari https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Tubuh fungsi async dapat dianggap sebagai dipecah dengan nol atau lebih ekspresi menunggu. Kode tingkat atas, hingga dan termasuk ekspresi menunggu pertama (jika ada), dijalankan secara sinkron.

... dan jadi itu akan sia-sia dan terburuk itu akan menambah beberapa overhead kinerja jika kode ditranspilasikan oleh babel misalnya karena akan menambahkan lebih banyak kode dan langkah hanya karena keberadaan kata kunci async

Misalnya, berikut ini:

async function foo() {
   return 1
}

... setara dengan:

function foo() {
   return Promise.resolve(1)
}

tetapi dengan perpustakaan ini,

async.mapLimit([1], 1, async v => v)

dan

async.mapLimit([1], 1, v => Promise.resolve(v)))

tidak setara!

Apakah halaman ini membantu?
0 / 5 - 0 peringkat