Lepsze zrozumienie funkcji wywołania zwrotnego w JavaScript

163

Rozumiem przekazywanie funkcji do innej funkcji jako wywołanie zwrotne i zlecanie jej wykonania, ale nie rozumiem najlepszej implementacji do tego. Szukam bardzo podstawowego przykładu, takiego jak ten:

var myCallBackExample = {
    myFirstFunction : function( param1, param2, callback ) {
        // Do something with param1 and param2.
        if ( arguments.length == 3 ) {
            // Execute callback function.
            // What is the "best" way to do this?
        }
    },
    mySecondFunction : function() {
        myFirstFunction( false, true, function() {
            // When this anonymous function is called, execute it.
        });
    }
};

W funkcji myFirstFunction, jeśli zwracam new callback (), to działa i wykonuje anonimową funkcję, ale wydaje mi się, że nie jest to właściwe podejście.


źródło
Poprawne w jakim sensie? Zwykle wywołania zwrotne są używane do obsługi zdarzeń - w szczególności wywołań Ajax, które są asynchroniczne - w zasadzie rzeczy, w których nie wiesz, kiedy (lub czy) nadejdzie odpowiedź.
cletus
2
tak przy okazji, argumenty są podobne do tablicy, ale nie do tablicy, więc nie możesz zrobić argument.length, ale możesz przekonwertować ją na tablicę za pomocą metody slice ...
paul
1
@paul, chociaż masz rację, że argumentsnie jest to tablica, nadal możesz odwołać się do jej długości jako arguments.length- spróbuj. Ta właściwość odnosi się do liczby faktycznie przekazanych argumentów, a niekoniecznie liczby parametrów w sygnaturze funkcji.
hotshot309

Odpowiedzi:

132

Możesz po prostu powiedzieć

callback();

Alternatywnie możesz użyć tej callmetody, jeśli chcesz dostosować wartość thisw wywołaniu zwrotnym.

callback.call( newValueForThis);

Wewnątrz funkcji thisbyłoby cokolwiek newValueForThisjest.

krosenvold
źródło
91

Powinieneś sprawdzić, czy callback istnieje i czy jest funkcją wykonywalną:

if (callback && typeof(callback) === "function") {
    // execute the callback, passing parameters as necessary
    callback();
}

Wiele bibliotek (jQuery, dojo itp.) Używa podobnego wzorca dla swoich funkcji asynchronicznych, a także node.js dla wszystkich funkcji asynchronicznych (nodejs zwykle przekazuje errori datado wywołania zwrotnego). Zajrzenie do ich kodu źródłowego mogłoby pomóc!

arunjitsingh
źródło
Dlaczego rzucasz callbackna string, a potem sprawdzasz jego typ? Czy to poprawi wydajność? Jest to podobne do sprawdzania typu, sprawdzania, czy przekonwertowana wartość logiczna zwraca wartość true, a następnie ponownego sprawdzania jej typu i porównywania z ciągiem znaków… Czy możesz wyjaśnić, dlaczego?
headacheCoder
Jestem ciekawy, dlaczego potrzebujesz pierwszego potwierdzenia do wywołania zwrotnego ... czy ma to sprawdzić wartość null czy undefined? Nie typeof(callback)osiągnąłbyś tego dla siebie? typeof(null) === "Object",typeof("undefined") === "undefined"
PJH,
1
Zwarcie AND. Jeśli wywołanie zwrotne nie istnieje, nie przejmuj się obliczaniem jego typu. Chociaż masz rację. Nie jest to potrzebne w przypadku typeof (), ale zrobię jsperf i zobaczę, czy zwarcie jest tego warte.
arunjitsingh
@headacheCoder - callbacknie jest rzutowany na ciąg, jego typ jest sprawdzany, aby sprawdzić, czy jest to funkcja, zanim zostanie wywołana. Kod przypuszczalnie akceptuje callbackjako argument i nie jest pewien, czy argument jest typu wywoływalnego - lub być może argumenty są różnego typu w celu zapewnienia formy polimorfizmu, w której kod mógłby różnie reagować na różne typeofargumenty.
LeeGee,
34

Istnieją 3 główne możliwości wykonania funkcji:

var callback = function(x, y) {
    // "this" may be different depending how you call the function
    alert(this);
};
  1. callback (argument_1, argument_2);
  2. callback.call (jakiś_obiekt, argument_1, argument_2);
  3. callback.apply (jakiś_obiekt, [argument_1, argument_2]);

Wybrana metoda zależy od tego, czy:

  1. Masz argumenty przechowywane w tablicy lub jako odrębne zmienne.
  2. Chcesz wywołać tę funkcję w kontekście jakiegoś obiektu. W takim przypadku użycie słowa kluczowego „this” w tym wywołaniu zwrotnym spowoduje odniesienie do obiektu przekazanego jako argument w funkcji call () lub apply (). Jeśli nie chcesz przekazywać kontekstu obiektu, użyj wartości null lub undefined. W tym drugim przypadku obiekt globalny zostałby użyty do „tego”.

Dokumenty dla funkcji Function.call , Function.apply

Ionuț G. Stan
źródło
6

Wywołania zwrotne dotyczą sygnałów, a „nowy” dotyczy tworzenia instancji obiektów.

W tym przypadku byłoby jeszcze bardziej odpowiednie wykonanie po prostu „callback ()”; niż „return new callback ()”, ponieważ i tak nie robisz nic z wartością zwracaną.

(A arguments.length == 3 test jest naprawdę niezgrabny, fwiw, lepiej sprawdzić, czy parametr wywołania zwrotnego istnieje i jest funkcją.)

annakata
źródło
6

właściwa realizacja to:

if( callback ) callback();

dzięki temu parametr wywołania zwrotnego jest opcjonalny.

faeb187
źródło
A jeśli argument wywołania zwrotnego nie jest funkcją?
Yaki Klein
2

Możesz użyć:

if (callback && typeof(callback) === "function") {
    callback();
}

Poniższy przykład jest trochę bardziej wszechstronny:

function mySandwich(param1, param2, callback) {
  alert('Started eating my sandwich.\n\nIt has: ' + param1 + ', ' + param2);
  var sandwich = {
      toppings: [param1, param2]
    },
    madeCorrectly = (typeof(param1) === "string" && typeof(param2) === "string") ? true : false;
  if (callback && typeof(callback) === "function") {
    callback.apply(sandwich, [madeCorrectly]);
  }
}

mySandwich('ham', 'cheese', function(correct) {
  if (correct) {
    alert("Finished eating my " + this.toppings[0] + " and " + this.toppings[1] + " sandwich.");
  } else {
    alert("Gross!  Why would I eat a " + this.toppings[0] + " and " + this.toppings[1] + " sandwich?");
  }
});

Hasan A Yousef
źródło
1

Oto podstawowy przykład wyjaśniający callback()funkcję w JavaScript:

var x = 0;

function testCallBack(param1, param2, callback) {
  alert('param1= ' + param1 + ', param2= ' + param2 + ' X=' + x);
  if (callback && typeof(callback) === "function") {
    x += 1;
    alert("Calla Back x= " + x);
    x += 1;
    callback();
  }
}

testCallBack('ham', 'cheese', function() {
  alert("Function X= " + x);
});

JSFiddle

BERGUIGA Mohamed Amine
źródło
1

function checkCallback(cb) {
  if (cb || cb != '') {
    if (typeof window[cb] === 'undefined') alert('Callback function not found.');
    else window[cb].call(this, Arg1, Arg2);
  }
}

Aamir Afridi
źródło