внутренний вызов js узла js не присваивает значение [duplicate]

Учитывая следующие примеры, почему 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. Не стесняйтесь улучшать этот вопрос и добавлять более упрощенные примеры, с которыми сообщество может идентифицироваться.
551
задан 7 March 2018 в 18:52

5 ответов

Ответ Фабрисио на месте; но я хотел дополнить его ответ чем-то менее техническим, который фокусируется на аналогии, чтобы помочь объяснить концепцию асинхронности.

Аналогия ...

Вчера работа, которую я делал, требовала некоторой информации от коллеги. Я позвонил ему; вот как пошла беседа:

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);
});

В настоящее время у вас есть код, похожий на этот:

119
ответ дан 15 August 2018 в 16:50
  • 1
    Черт возьми, Боб, будьте в следующий раз! В любом случае хорошая аналогия! – Jimmy Knoot 31 October 2016 в 16:40
  • 2
    большая аналогия и объяснение – Anupam 21 April 2017 в 13:21
  • 3
    Приобретено, потому что это помогает мне понять асинхронный и синхронный из фона не программирования. Благодарю. – Mindthetic 24 May 2017 в 12:34

Во всех этих сценариях outerScopeVar изменяется или назначается значение асинхронно или происходит в более позднее время (ожидание или прослушивание какого-либо события), для которого текущее выполнение не будет ждать. Таким образом, все эти случаи текущего потока выполнения приводит к outerScopeVar = undefined

Давайте обсудим каждый пример (я отметил часть, которая называется асинхронно или задерживается для возникновения некоторых событий):

асинхронно [ ! d4]

Здесь мы регистрируем eventlistner, который будет выполняться на этом конкретном событии. Загрузите изображение. Затем текущее выполнение продолжается со следующими строками img.src = 'lolcat.png'; и alert(outerScopeVar); тем временем событие может не произойти. т. е. funtion img.onload ожидают, что упомянутое изображение будет загружаться, как можно скорее. Это будет происходить во всех следующих случаях: событие может отличаться.

2.

Здесь играет роль тайм-аут , который вызывается обработчиком по истечении указанного времени. Здесь 0, но все же он регистрирует асинхронное событие, которое будет добавлено к последней позиции Event Queue для выполнения, что делает гарантированную задержку.

2.

enter image description here [!d8] На этот раз ajax callback.

4.

enter image description here [!d8]

Узел можно рассматривать как король асинхронного кодирования. Если отмеченная функция зарегистрирована как обработчик обратного вызова, которая будет выполнена после чтения указанного файла.

4 .

Очевидное обещание (что-то будет сделано в будущем) является асинхронным. см.

https://www.quora.com/Whats-the-difference-between-a-promise-and-a-callback-in-Javascript

8
ответ дан 15 August 2018 в 16:50

Чтобы указать очевидное, чаша представляет outerScopeVar.

Асинхронные функции будут такими, как ...

45
ответ дан 15 August 2018 в 16:50
  • 1
    Принимая во внимание, что попытка заставить асинхронную функцию действовать синхронно, будет пытаться выпить кофе через 1 секунду, а его вылить на колени на 1 минуту. – Teepeemm 10 December 2015 в 19:22
  • 2
    Если бы он заявлял очевидное, я не думаю, что вопрос был бы задан, нет? – broccoli2000 3 July 2016 в 16:10
  • 3
    @ broccoli2000 Тем самым я не имел в виду, что вопрос был очевиден, но очевидно, что чаша представляет на рисунке :) – Johannes Fahrenkrug 29 September 2016 в 22:24
  • 4
    Хорошая картинка :) ха-ха – V. Kalyuzhnyu 22 April 2017 в 09:45

Вот более краткий ответ для людей, которые ищут быструю ссылку, а также некоторые примеры с использованием обещаний и асинхронных / ожидающих.

Начните с наивного подхода (который не работает) для функция, вызывающая асинхронный метод (в данном случае 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();
55
ответ дан 15 August 2018 в 16:50
  • 1
    Ваш пример в Promises - это в основном то, что я искал, последние несколько часов. Ваш пример прекрасный и объясняет Обещания в то же время. Почему это нигде больше не ошеломляет. – Vincent P 8 January 2016 в 12:40
  • 2
    Все в порядке, но что, если вам нужно вызвать getMessage () с параметрами? Как вы могли бы написать выше в этом сценарии? – Chiwda 1 August 2017 в 20:51
  • 3
    @Chiwda Вы просто положили параметр обратного вызова последним: function getMessage(param1, param2, callback) {...}. – JohnnyHK 1 August 2017 в 22:09

Другие ответы превосходны, и я просто хочу дать прямой ответ на этот вопрос. Простое ограничение асинхронных вызовов 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);
});

Весь код, зависящий от асинхронных вызовов, перемещается внутри асинхронного блока или ожидает асинхронных вызовов.

11
ответ дан 15 August 2018 в 16:50

Другие вопросы по тегам:

Похожие вопросы: