Учитывая следующие примеры, почему outerScopeVar не определено во всех случаях?
var outerScopeVar;
var img = document.createElement('img');
img.onload = function() {
outerScopeVar = this.width;
};
img.src = 'lolcat.png';
alert(outerScopeVar);
var outerScopeVar;
setTimeout(function() {
outerScopeVar = 'Hello Asynchronous World!';
}, 0);
alert(outerScopeVar);
// Example using some jQuery
var outerScopeVar;
$.post('loldog', function(response) {
outerScopeVar = response;
});
alert(outerScopeVar);
// Node.js example
var outerScopeVar;
fs.readFile('./catdog.html', function(err, data) {
outerScopeVar = data;
});
console.log(outerScopeVar);
// with promises
var outerScopeVar;
myPromise.then(function (response) {
outerScopeVar = response;
});
console.log(outerScopeVar);
// geolocation API
var outerScopeVar;
navigator.geolocation.getCurrentPosition(function (pos) {
outerScopeVar = pos;
});
console.log(outerScopeVar);
Почему он выводит undefined во всех этих примерах? Мне не нужны обходные пути, я хочу знать, почему это происходит.
Примечание. Это канонический вопрос для асинхронности JavaScript. Не стесняйтесь улучшать этот вопрос и добавлять более упрощенные примеры, с которыми сообщество может идентифицироваться.Ответ Фабрисио на месте; но я хотел дополнить его ответ чем-то менее техническим, который фокусируется на аналогии, чтобы помочь объяснить концепцию асинхронности.
Вчера работа, которую я делал, требовала некоторой информации от коллеги. Я позвонил ему; вот как пошла беседа:
Me: Привет, Боб, мне нужно знать, как мы foo'd bar'd на прошлой неделе. Джим хочет сообщить об этом, и вы единственный, кто знает подробности об этом.Me: Привет, Боб, мне нужно знать, как мы foo'd bar'd на прошлой неделе. Джим хочет сообщить об этом, и вы единственный, кто знает подробности об этом.
Me : Это замечательно Боб. Верните мне кольцо, когда у вас есть информация!Боб: Конечно, но мне понадобится около 30 минут?
На данный момент я повесил трубку. Поскольку мне нужна была информация от Боба, чтобы закончить мой отчет, я оставил отчет и пошел за кофе вместо этого, затем я догнал какой-то адрес электронной почты. 40 минут спустя (Боб медленно), Боб позвонил и дал мне информацию, в которой я нуждался. На этом этапе я возобновил свою работу с моим докладом, так как у меня была вся информация, в которой я нуждался.
Me : Это замечательно Боб. Дайте мне кольцо, когда у вас есть информация!
Боб: Конечно, но мне понадобится около 30 минут?Боб: Конечно, но мне понадобится около 30 минут?
Представьте, если бы разговор пошел так:
Me: Привет, Боб, мне нужно знать, как мы foo 'd the bar'd last неделю. Джим хочет сообщить об этом, и ты единственный, кто знает подробности об этом.
Я: Это замечательно Боб. Я буду ждать.
var outerScopeVar;
var img = document.createElement('img');
// Here we register the callback function.
img.onload = function() {
// Code within this function will be executed once the image has loaded.
outerScopeVar = this.width;
};
// But, while the image is loading, JavaScript continues executing, and
// processes the following lines of JavaScript.
img.src = 'lolcat.png';
alert(outerScopeVar);
И я сидел и ждал. И ждал. И ждал. В течение 40 минут. Не делать ничего, кроме ожидания. В конце концов, Боб дал мне информацию, мы повесили трубку, и я закончил свой отчет. Но я потерял 40 минут производительности.
Это именно то, что происходит во всех примерах нашего вопроса. Загрузка изображения, загрузка файла с диска и запрос страницы через AJAX - это медленные операции (в контексте современных вычислений).
Вместо ответа ответ Фабрисио находится на месте; но я хотел дополнить его ответ чем-то менее техническим, который фокусируется на аналогии, чтобы помочь объяснить концепцию asynchronicity для выполнения этих медленных операций, JavaScript позволяет зарегистрировать функцию обратного вызова, которая будет выполняться при медленной работе завершено. Тем временем, однако, JavaScript будет продолжать выполнять другой код. Тот факт, что JavaScript выполняет bar , ожидая завершения медленной операции, делает поведение Bob . Если бы JavaScript выполнил операцию до завершения любого другого кода, это было бы Me .
var img = document.createElement('img');
img.onload = function() {
var localScopeVar = this.width;
alert(localScopeVar);
};
img.src = 'lolcat.png';
В приведенном выше коде мы просим JavaScript load lolcat.png, что является операцией sloooow. Функция обратного вызова будет выполнена после выполнения этой медленной операции, но в то же время JavaScript будет продолжать обрабатывать следующие строки кода; т.е. alert(outerScopeVar).
Вот почему мы видим предупреждение, показывающее undefined; так как alert() обрабатывается немедленно, а не после загрузки изображения.
Чтобы исправить наш код, все, что нам нужно сделать, - переместить код alert(outerScopeVar) в функцию обратного вызова. Вследствие этого нам больше не нужна переменная outerScopeVar, объявленная как глобальная переменная.
Вы Me видите, что обратный вызов указан как функция, потому что это единственный способ JavaScript в JavaScript определить некоторый код, но не выполнить его до конца.
function getWidthOfImage(src) {
var outerScopeVar;
var img = document.createElement('img');
img.onload = function() {
outerScopeVar = this.width;
};
img.src = src;
return outerScopeVar;
}
var width = getWidthOfImage('lolcat.png');
alert(width);
Поэтому во всех наших примерах function() { /* Do something */ } является обратным вызовом; чтобы исправить все примеры, все, что нам нужно сделать, - это переместить код, который нуждается в ответе операции там!
* Технически вы можете использовать eval(), но eval() является злом для Эта цель
function getWidthOfImage(src, cb) {
var img = document.createElement('img');
img.onload = function() {
cb(this.width);
};
img.src = src;
}
getWidthOfImage('lolcat.png', function (width) {
alert(width);
});
В настоящее время у вас есть код, похожий на этот:
Во всех этих сценариях outerScopeVar изменяется или назначается значение асинхронно или происходит в более позднее время (ожидание или прослушивание какого-либо события), для которого текущее выполнение не будет ждать. Таким образом, все эти случаи текущего потока выполнения приводит к outerScopeVar = undefined
Давайте обсудим каждый пример (я отметил часть, которая называется асинхронно или задерживается для возникновения некоторых событий):
асинхронно [ ! d4]
Здесь мы регистрируем eventlistner, который будет выполняться на этом конкретном событии. Загрузите изображение. Затем текущее выполнение продолжается со следующими строками img.src = 'lolcat.png'; и alert(outerScopeVar); тем временем событие может не произойти. т. е. funtion img.onload ожидают, что упомянутое изображение будет загружаться, как можно скорее. Это будет происходить во всех следующих случаях: событие может отличаться.
2.
Здесь играет роль тайм-аут , который вызывается обработчиком по истечении указанного времени. Здесь 0, но все же он регистрирует асинхронное событие, которое будет добавлено к последней позиции Event Queue для выполнения, что делает гарантированную задержку.
2.
[!d8] На этот раз ajax callback.
4.
Узел можно рассматривать как король асинхронного кодирования. Если отмеченная функция зарегистрирована как обработчик обратного вызова, которая будет выполнена после чтения указанного файла.
4 .
Очевидное обещание (что-то будет сделано в будущем) является асинхронным. см.
https://www.quora.com/Whats-the-difference-between-a-promise-and-a-callback-in-Javascript
Чтобы указать очевидное, чаша представляет outerScopeVar.
Асинхронные функции будут такими, как ...
Вот более краткий ответ для людей, которые ищут быструю ссылку, а также некоторые примеры с использованием обещаний и асинхронных / ожидающих.
Начните с наивного подхода (который не работает) для функция, вызывающая асинхронный метод (в данном случае setTimeout) и возвращает сообщение:
function getMessage() {
var outerScopeVar;
setTimeout(function() {
outerScopeVar = 'Hello asynchronous world!';
}, 0);
return outerScopeVar;
}
console.log(getMessage());
undefined регистрируется в этом случае, потому что getMessage возвращается до вызова setTimeout и обновления outerScopeVar.
Двумя основными способами его решения являются обратные вызовы и обещания:
обратные вызовы
Изменение здесь getMessage принимает параметр callback, который будет вызываться для доставки результатов обратно к вызывающему коду после его появления.
function getMessage(callback) {
setTimeout(function() {
callback('Hello asynchronous world!');
}, 0);
}
getMessage(function(message) {
console.log(message);
});
Обещает
Обещания предоставляют альтернативу который является более гибким, чем обратные вызовы, потому что их можно комбинировать естественно для координации нескольких асинхронных операций. Стандартная реализация Promises / A + изначально представлена в node.js (0.12+) и во многих современных браузерах, но также реализована в таких библиотеках, как Bluebird и Q.
function getMessage() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('Hello asynchronous world!');
}, 0);
});
}
getMessage().then(function(message) {
console.log(message);
});
Promises
jQuery предоставляет функциональность, похожую на обещания с ее отсрочкой.
function getMessage() {
var deferred = $.Deferred();
setTimeout(function() {
deferred.resolve('Hello asynchronous world!');
}, 0);
return deferred.promise();
}
getMessage().done(function(message) {
console.log(message);
});
async / await
Если ваша среда JavaScript включает поддержку Promises / A + и await (например, Node.js 7.6+), вы можете синхронно использовать обещания внутри функций async:
function getMessage () {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('Hello asynchronous world!');
}, 0);
});
}
async function main() {
let message = await getMessage();
console.log(message);
}
main();
Другие ответы превосходны, и я просто хочу дать прямой ответ на этот вопрос. Простое ограничение асинхронных вызовов jQuery
Все вызовы ajax (включая $.get или $.post или $.ajax) асинхронны.
Учитывая ваш пример
var outerScopeVar; //line 1
$.post('loldog', function(response) { //line 2
outerScopeVar = response;
});
alert(outerScopeVar); //line 3
Выполнение кода начинается с строки 1, объявляет переменную и триггеры и асинхронный вызов в строке 2 (т. е. пост-запрос) и продолжает выполнение из строки 3, не дожидаясь, когда почтовый запрос завершит выполнение .
Допустим, что запрос на отправку занимает 10 секунд, значение outerScopeVar будет установлено только после этих 10 секунд.
Чтобы попробовать,
var outerScopeVar; //line 1
$.post('loldog', function(response) { //line 2, takes 10 seconds to complete
outerScopeVar = response;
});
alert("Lets wait for some time here! Waiting is fun"); //line 3
alert(outerScopeVar); //line 4
Теперь, когда вы выполните это, вы получите предупреждение в строке 3. Теперь подождите некоторое время, пока не убедитесь, что почтовый запрос вернул некоторое значение. Затем, когда вы нажмете «ОК», в окне предупреждения следующее предупреждение выведет ожидаемое значение, потому что вы его ждали.
В сценарии реальной жизни код становится,
var outerScopeVar;
$.post('loldog', function(response) {
outerScopeVar = response;
alert(outerScopeVar);
});
Весь код, зависящий от асинхронных вызовов, перемещается внутри асинхронного блока или ожидает асинхронных вызовов.