Gaya JavaScript untuk callback opsional

93

Saya memiliki beberapa fungsi yang terkadang (tidak selalu) akan menerima panggilan balik dan menjalankannya. Apakah memeriksa apakah callback didefinisikan / berfungsi dengan gaya yang baik atau apakah ada cara yang lebih baik?

Contoh:

function save (callback){
   .....do stuff......
   if(typeof callback !== 'undefined'){
     callback();
   };
};
henry.oswald
sumber
di peramban modern, Anda bisa menggunakannya, typeof callback !== undefinedjadi tinggalkan'
Snickbrack
1
dan jika Anda hanya menelepon save()? Bukankah itu akan memberikan peringatan kesalahan atau linting karena ada argumen yang hilang? Atau tidak apa-apa dan panggilan balik sederhana undefined?
João Pimentel Ferreira

Jawaban:

139

Saya pribadi lebih suka

typeof callback === 'function' && callback();

Namun typeofperintahnya cerdik dan hanya boleh digunakan untuk "undefined"dan"function"

Masalah dengan yang typeof !== undefinedadalah bahwa pengguna mungkin mengirimkan nilai yang didefinisikan dan bukan fungsi

Raynos
sumber
3
typeoftidak cerdik. Terkadang tidak jelas, tetapi saya tidak akan menyebutnya cerdik. Setuju, meskipun, bahwa jika Anda akan memanggil callback sebagai sebuah fungsi, yang terbaik adalah memeriksa bahwa itu sebenarnya sebuah fungsi dan bukan, Anda tahu, sebuah angka. :-)
TJ Crowder
1
@TJCrowder cerdik mungkin kata yang salah, itu didefinisikan dengan baik tetapi tidak berguna karena tinju dan kembali "object"95% dari waktu.
Raynos
1
typeoftidak menyebabkan tinju. typeof "foo"adalah "string", tidak "object". Ini sebenarnya satu-satunya cara nyata untuk mengetahui apakah Anda berurusan dengan string primitif atau Stringobjek. (Mungkin Anda sedang memikirkan Object.prototype.toString, yang sangat berguna , tetapi memang menyebabkan tinju.)
TJ Crowder
4
Penggunaan idiomatik yang luar biasa dari &&omong-omong.
TJ Crowder
@TJCrowder Anda ada benarnya, saya tidak tahu apa nilainya membandingkan string primitif dan objek String kotak. Juga saya setuju .toStringitu indah untuk menemukan [[Class]].
Raynos
50

Anda juga bisa melakukan:

var noop = function(){}; // do nothing.

function save (callback){
   callback = callback || noop;
   .....do stuff......
};

Ini sangat berguna jika Anda kebetulan menggunakannya callbackdi beberapa tempat.

Selain itu, jika Anda menggunakan jQuery, Anda sudah memiliki fungsi seperti itu, namanya $ .noop

Pablo Fernandez
sumber
4
Menurut saya, ini adalah solusi paling elegan.
Nicolas Le Thierry d'Ennequin
2
Sepakat. Ini juga membuat pengujian lebih mudah, karena tidak ada kondisi if.
5
tapi ini tidak mengatasi masalah tipe bukan? bagaimana jika saya melewatkan array? atau String?
Zerho
1
Sederhanakan kecallback = callback || function(){};
NorCalKnockOut
1
Anda juga dapat membuat argumen opsional dengan memberinya nilai default dalam deklarasi:function save (callback=noop) { ...
Keith
39

Lakukan saja

if (callback) callback();

Saya lebih suka memanggil callback jika disediakan, apa pun jenisnya. Jangan biarkan gagal secara diam-diam, sehingga pelaksana tahu bahwa dia memberikan argumen yang salah dan dapat memperbaikinya.

ninja123
sumber
11

ECMAScript 6

// @param callback Default value is a noop fn.
function save(callback = ()=>{}) {
   // do stuff...
   callback();
}
Lucio
sumber
3

Daripada menjadikan panggilan balik opsional, tetapkan saja default dan panggil itu apa pun yang terjadi

const identity = x =>
  x

const save (..., callback = identity) {
  // ...
  return callback (...)
}

Saat digunakan

save (...)              // callback has no effect
save (..., console.log) // console.log is used as callback

Gaya seperti itu disebut gaya penerusan-lanjutan . Berikut adalah contoh nyata combinations, yang menghasilkan semua kemungkinan kombinasi input Array

const identity = x =>
  x

const None =
  Symbol ()

const combinations = ([ x = None, ...rest ], callback = identity) =>
  x === None
    ? callback ([[]])
    : combinations
        ( rest
        , combs =>
            callback (combs .concat (combs .map (c => [ x, ...c ])))
        )

console.log (combinations (['A', 'B', 'C']))
// [ []
// , [ 'C' ]
// , [ 'B' ]
// , [ 'B', 'C' ]
// , [ 'A' ]
// , [ 'A', 'C' ]
// , [ 'A', 'B' ]
// , [ 'A', 'B', 'C' ]
// ]

Karena combinationsdidefinisikan dalam gaya penerusan-penerusan, panggilan di atas secara efektif sama

combinations (['A', 'B', 'C'], console.log)
// [ []
// , [ 'C' ]
// , [ 'B' ]
// , [ 'B', 'C' ]
// , [ 'A' ]
// , [ 'A', 'C' ]
// , [ 'A', 'B' ]
// , [ 'A', 'B', 'C' ]
// ]

Kami juga dapat meneruskan kelanjutan kustom yang melakukan hal lain dengan hasilnya

console.log (combinations (['A', 'B', 'C'], combs => combs.length))
// 8
// (8 total combinations)

Gaya passing lanjutan dapat digunakan dengan hasil yang sangat elegan

const first = (x, y) =>
  x

const fibonacci = (n, callback = first) =>
  n === 0
    ? callback (0, 1)
    : fibonacci
        ( n - 1
        , (a, b) => callback (b, a + b)
        )
        
console.log (fibonacci (10)) // 55
// 55 is the 10th fibonacci number
// (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...)

Terima kasih
sumber
1

Saya sangat lelah melihat cuplikan yang sama berulang kali saya menulis ini:

  var cb = function(g) {
    if (g) {
      var args = Array.prototype.slice.call(arguments); 
      args.shift(); 
      g.apply(null, args); 
    }
  };

Saya memiliki ratusan fungsi yang melakukan hal-hal seperti

  cb(callback, { error : null }, [0, 3, 5], true);

atau terserah...

Saya skeptis terhadap keseluruhan strategi "pastikan fungsinya". Satu-satunya nilai yang sah adalah fungsi atau falsy. Jika seseorang memasukkan angka bukan nol atau string tidak kosong, apa yang akan Anda lakukan? Bagaimana mengabaikan masalah menyelesaikannya?

Malvolio
sumber
Fungsi mungkin kelebihan beban untuk menerima beberapa nilai sebagai argumen pertama, fungsi sebagai argumen pertama atau kedua parameter. Ada kasus penggunaan untuk memeriksa apakah itu sebuah fungsi. Juga lebih baik untuk mengabaikan parameter yang salah kemudian melemparkan kesalahan karena menjalankan non-fungsi
Raynos
@Raynos - Itu kasus penggunaan yang sangat, sangat spesifik. JavaScript, karena diketik lemah, tidak pandai membedakan jenis, dan biasanya lebih baik meneruskan parameter bernama daripada mencoba membedakan jenis dan menebak apa yang diinginkan pemanggil:save( { callback : confirmProfileSaved, priority: "low" })
Malvolio
Saya tidak setuju, ada cukup kemampuan untuk memeriksa tipe. Saya lebih suka metode overloading, tapi itu preferensi pribadi. Ini adalah hal yang sering dilakukan jQuery.
Raynos
@Raynos - Lebih buruk untuk mengabaikan parameter yang salah kemudian melempar kesalahan karena menjalankan non-fungsi. Pertimbangkan kasus tipikal: fungsi library melakukan beberapa aktivitas asinkron dan fungsi pemanggil perlu diberi tahu. Jadi, pengguna yang tidak canggih, mengabaikan dan gagal tampak sama: aktivitas tampaknya tidak pernah selesai. Namun, untuk pengguna yang canggih (misalnya, programmer asli), kegagalan memberikan pesan kesalahan dan jejak tumpukan yang mengarah langsung ke masalah; mengabaikan parameter berarti analisis yang membosankan untuk menentukan apa yang menyebabkan "tidak ada" terjadi.
Malvolio
ada trade-off yang harus dibuat. Melempar kesalahan akan merusak waktu proses, tetapi mengabaikannya akan membiarkan kode lain di sekitarnya dijalankan seperti biasa.
Raynos
1

Fungsi yang valid didasarkan pada prototipe Fungsi, gunakan:

if (callback instanceof Function)

untuk memastikan panggilan balik adalah sebuah fungsi

G. Moore
sumber
0

Jika kriteria untuk menjalankan callback adalah apakah itu ditentukan atau tidak, maka Anda baik-baik saja. Juga, saya sarankan untuk memeriksa apakah itu benar-benar fungsi tambahan.

Mrchief
sumber
0

Saya telah dengan tulus pindah ke skrip kopi dan menemukan argumen default adalah cara yang bagus untuk menyelesaikan masalah ini

doSomething = (arg1, arg2, callback = ()->)->
    callback()
henry.oswald
sumber
0

Ini dapat dengan mudah dilakukan dengan ArgueJS :

function save (){
  arguments = __({callback: [Function]})
.....do stuff......
  if(arguments.callback){
    callback();
  };
};
zVictor
sumber