Jak struktura JavaScript callback tak, że zakres funkcji jest utrzymywany prawidłowo
Używam XMLHttpRequest i chcę uzyskać dostęp do zmiennej lokalnej w funkcji wywołania zwrotnego success.
Oto kod:
function getFileContents(filePath, callbackFn) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
callbackFn(xhr.responseText);
}
}
xhr.open("GET", chrome.extension.getURL(filePath), true);
xhr.send();
}
I chcę to tak nazywać:
var test = "lol";
getFileContents("hello.js", function(data) {
alert(test);
});
Tutaj, test
byłoby poza zakresem funkcji zwrotnej, ponieważ tylko zmienne funkcji zamykającej są dostępne wewnątrz funkcji zwrotnej. Jaki jest najlepszy sposób, aby przekazać test
do funkcji callback, aby alert(test);
wyświetlał test
poprawnie?
Edytuj:
Teraz, jeśli mam następujący kod wywołujący funkcję zdefiniowaną powyżej:
for (var test in testers) {
getFileContents("hello.js", function(data) {
alert(test);
});
}
Kod alert(test);
wyświetla tylko ostatnią wartość test
z pętli for
. Jak to zrobić, aby wydrukowała wartość test
w czasie, w którym funkcja getFileContents
została wywołana? (Chciałbym to zrobić bez zmiany getFileContents
, ponieważ jest to bardzo ogólna funkcja pomocnicza i nie chcę, aby była specyficzna, przekazując do niej określoną zmienną, taką jak test
.
4 answers
Z kodem, który podałeś test
nadal będzie w zasięgu wewnątrz wywołania zwrotnego. xhr
nie będzie, poza xhr.responseText
przekazaniem jako data
.
Aktualizacja z komentarza :
Zakładając, że Twój kod wygląda mniej więcej tak:
for (var test in testers)
getFileContents("hello"+test+".js", function(data) {
alert(test);
});
}
W trakcie działania skryptu test
zostaną przypisane wartości kluczy w testers
- getFileContents
jest wywoływany za każdym razem, co uruchamia żądanie w tle. Po zakończeniu żądania wywołuje callback. test
będzie zawiera wartość końcową z pętli, ponieważ pętla ta została już wykonana.
Istnieje technika, której możesz użyć, zwana zamknięciem, która naprawi tego rodzaju problem. Możesz utworzyć funkcję, która zwraca funkcję zwrotną, tworząc nowy zakres, w którym możesz trzymać swoje zmienne za pomocą:
for (var test in testers) {
getFileContents("hello"+test+".js",
(function(test) { // lets create a function who has a single argument "test"
// inside this function test will refer to the functions argument
return function(data) {
// test still refers to the closure functions argument
alert(test);
};
})(test) // immediately call the closure with the current value of test
);
}
To w zasadzie stworzy nowy zakres (wraz z naszą nową funkcją), który "utrzyma" wartość test
.
Inny sposób pisania to samo:
for (var test in testers) {
(function(test) { // lets create a function who has a single argument "test"
// inside this function test will refer to the functions argument
// not the var test from the loop above
getFileContents("hello"+test+".js", function(data) {
// test still refers to the closure functions argument
alert(test);
});
})(test); // immediately call the closure with the value of `test` from `testers`
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-25 10:00:20
JavaScript używa zakresów leksykalnych , co zasadniczo oznacza, że twój drugi przykład kodu będzie działał tak,jak zamierzasz.
Rozważ poniższy przykład, zapożyczony z przewodnika Davida Flanagana Definitive Guide1:
var x = "global";
function f() {
var x = "local";
function g() { alert(x); }
g();
}
f(); // Calling this function displays "local"
Należy również pamiętać, że w przeciwieństwie do C, C++ i Javy, JavaScript nie ma zakresu na poziomie bloków.
Ponadto możesz być również zainteresowany zapoznaniem się z poniższym artykułem, który bardzo polecam:
1 David Flanagan: JavaScript-The Definitive Guide , Wydanie Czwarte, Strona 48.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:51
W tym scenariuszu test zostanie rozwiązany zgodnie z oczekiwaniami, ale wartość this
może być inna. Zwykle, aby zachować zakres, Należy uczynić go parametrem funkcji asynchronicznej w następujący sposób:
function getFileContents(filePath, callbackFn, scope) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
callbackFn.call(scope, xhr.responseText);
}
}
xhr.open("GET", chrome.extension.getURL(filePath), true);
xhr.send();
}
//then to call it:
var test = "lol";
getFileContents("hello.js", function(data) {
alert(test);
}, this);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-24 22:54:39
Miałem podobny problem. Mój kod wyglądał tak:
for (var i=0; i<textFilesObj.length; i++)
{
var xmlHttp=new XMLHttpRequest();
var name = "compile/" + textFilesObj[i].fileName;
var content = textFilesObj[i].content;
xmlHttp.onreadystatechange=function()
{
if(xmlHttp.readyState==4)
{
var responseText = xmlHttp.responseText;
Debug(responseText);
}
}
xmlHttp.open("POST","save1.php",true);
xmlHttp.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xmlHttp.send("filename="+name+"&text="+encodeURIComponent(content));
}
Wyjście było często tekstem odpowiedzi ostatniego obiektu (ale nie zawsze). W rzeczywistości był to rodzaj przypadkowy i nawet liczba odpowiedzi była różna (dla stałego wejścia). Okazuje się, że funkcja powinna być zapisana:
xmlHttp.onreadystatechange=function()
{
if(this.readyState==4)
{
var responseText = this.responseText;
Debug(responseText);
}
}
Zasadniczo w pierwszej wersji obiekt "xmlHttp" zostanie ustawiony na wersję w bieżącym etapie pętli. Większość czasu pętla skończyłaby się przed którymkolwiek z żądania AJAX zostały zakończone, więc w tym przypadku "xmlHttp" odnosił się do ostatniej instancji pętli. Jeśli to ostateczne żądanie zakończy się przed innymi żądaniami, wszystkie one wydrukują odpowiedź z ostatniego żądania za każdym razem, gdy zmieni się ich stan gotowości (nawet jeśli ich stan gotowości wynosił
Rozwiązaniem jest zastąpienie "XMLHTTP "przez " this", aby funkcja wewnętrzna odnosiła się do prawidłowej instancji obiektu request za każdym razem, gdy wywołane jest wywołanie zwrotne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-27 00:07:21