Почему последняя строка печатает 2 вместо 8? [Дубликат]

Если вы не используете jQuery в своем коде, этот ответ для вас

Ваш код должен быть чем-то вроде этого:

function foo() {
    var httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
    return httpRequest.responseText;
}

var result = foo(); // always ends up being 'undefined'

Феликс Клинг отлично справился с написанием ответа для людей, использующих jQuery для AJAX, я решил предоставить альтернативу для людей, которые этого не делают.

( Примечание. используя новый API fetch, угловые или обещания, я добавил еще один ответ ниже )


То, с чем вы столкнулись

Это краткое резюме «Объяснение проблемы» из другого ответа, если вы не уверены, прочитав это, прочитайте это.

A в AJAX означает асинхронность. Это означает, что отправка запроса (или, скорее, получение ответа) вынимается из обычного потока выполнения. В вашем примере .send немедленно возвращается, а следующий оператор return result; выполняется до того, как функция, которую вы передали, когда был вызван обратный вызов success.

Это означает когда вы возвращаетесь, слушатель, который вы определили, еще не выполнил, что означает, что возвращаемое вами значение не было определено.

Вот простая аналогия

function getFive(){ 
    var a;
    setTimeout(function(){
         a=5;
    },10);
    return a;
}

(Fiddle)

Возвращаемое значение a - undefined так как часть a=5 еще не выполнена. AJAX действует так, вы возвращаете значение до того, как сервер получил возможность сообщить вашему браузеру, что это за значение.

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

function onComplete(a){ // When the code completes, do this
    alert(a);
}

function getFive(whenDone){ 
    var a;
    setTimeout(function(){
         a=5;
         whenDone(a);
    },10);
}

Это называется CPS . В основном, мы передаем getFive действие, которое необходимо выполнить, когда оно завершается, мы сообщаем нашему кодексу, как реагировать, когда событие завершается (например, наш вызов AJAX или в этом случае время ожидания).

Использование будет:

getFive(onComplete);

Который должен предупредить «5» на экране. (Fiddle) .

Возможные решения

Существуют два способа решения этой проблемы:

  1. Сделать AJAX синхронный вызов (позволяет называть его SJAX).
  2. Реструктурируйте свой код для правильной работы с обратными вызовами.

1. Синхронный AJAX - Не делайте этого !!

Что касается синхронного AJAX, не делайте этого! Ответ Феликса вызывает некоторые веские аргументы в пользу того, почему это плохая идея. Подводя итог, он заморозит браузер пользователя, пока сервер не вернет ответ и не создаст очень плохой пользовательский интерфейс. Вот еще краткое резюме из MDN о том, почему:

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

Короче говоря, синхронные запросы блокируют выполнение кода ... ... это может вызвать серьезные проблемы ...

Если вы имеете , вы можете передать флаг: Вот как это сделать:

var request = new XMLHttpRequest();
request.open('GET', 'yourURL', false);  // `false` makes the request synchronous
request.send(null);

if (request.status === 200) {// That's HTTP for 'ok'
  console.log(request.responseText);
}

2. Код реструктуризации

Пусть ваша функция принимает обратный вызов. В примере код foo может быть сделан для принятия обратного вызова. Мы сообщим нашему кодексу, как отреагировали , когда foo завершает работу.

Итак:

var result = foo();
// code that depends on `result` goes here

Становится:

foo(function(result) {
    // code that depends on `result`
});

Здесь мы передали анонимную функцию, но мы могли бы так же легко передать ссылку на существующую , чтобы он выглядел следующим образом:

function myHandler(result) {
    // code that depends on `result`
}
foo(myHandler);

Для получения дополнительной информации о том, как выполняется этот вид обратного вызова, проверьте ответ Felix.

Теперь давайте определим сам foo, чтобы действовать соответственно

function foo(callback) {
    var httpRequest = new XMLHttpRequest();
    httpRequest.onload = function(){ // when the request is loaded
       callback(httpRequest.responseText);// we're calling our method
    };
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
}

(скрипка)

Теперь мы сделали нашу функцию foo принять действие, которое будет выполняться, когда AJAX завершится успешно, мы можем продолжить это, проверив, не является ли статус ответа не 200 и действует соответственно (создайте обработчик сбоя и т. д.). Эффективное решение нашей проблемы.

Если вам все еще трудно понять это , прочитайте руководство по началу работы AJAX в MDN.

45
задан eje211 1 January 2015 в 17:33
поделиться

8 ответов

Идея состоит в том, что вы указываете на блоки памяти

+----+----+----+----+----+----+
| 06 | 07 | 08 | 09 | 10 | 11 | mem
+----+----+----+----+----+----+
| 18 | 24 | 17 | 53 | -7 | 14 | data
+----+----+----+----+----+----+

Если у вас есть int* p = &(array[5]), тогда *p будет 14. Переход p=p-3 сделает *p равным 17 .

Итак, если у вас есть int* p = &(array[5]) и int *q = &(array[3]), тогда p-q должно быть 2, потому что указатели указывают на память, разделяющую 2 блока.

При работе с необработанной памятью (массивы, списки, карты и т. д.) нарисуйте много ящиков! Это действительно помогает!

65
ответ дан corsiKa 5 September 2018 в 07:36
поделиться

При применении арифметических операций над указателями определенного типа вы всегда хотите, чтобы результирующий указатель указывал на адрес «допустимый» (то есть правильный размер шага) адрес памяти относительно исходной точки. Это очень удобный способ доступа к данным в памяти независимо от базовой архитектуры.

Если вы хотите использовать другой «шаг-размер», вы всегда можете указать указатель на нужный тип:

int a = 5;
int* pointer_int = &a;
double* pointer_double = (double*)pointer_int; /* totally useless in that case, but it works */
1
ответ дан das_weezul 5 September 2018 в 07:36
поделиться

Потому что все в области указателя - это смещения. Когда вы говорите:

int array[10];
array[7] = 42;

То, что вы на самом деле говорите во второй строке:

*( &array[0] + 7 ) = 42;

Буквально переводится как:

* = "what's at"
(
  & = "the address of"
  array[0] = "the first slot in array"
  plus 7
)
set that thing to 42

И если мы можем добавить 7, чтобы сделать точку смещения в нужном месте, мы должны иметь возможность иметь противоположное место, иначе у нас нет симметрии в нашей математике. Если:

&array[0] + 7 == &array[7]

Затем для здравомыслия и симметрии:

&array[7] - &array[0] == 7
34
ответ дан eruciform 5 September 2018 в 07:36
поделиться

Скажем, у вас есть массив из 10 целых чисел:

int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

Затем вы берете указатель на intArray:

int *p = intArray;

Затем вы увеличиваете p:

p++;

Что вы ожидаете, потому что p начинается с intArray[0], для приращенного значения p будет intArray[1]. Вот почему арифметика указателей работает так. См. здесь код.

7
ответ дан Jeff Kelley 5 September 2018 в 07:36
поделиться

Это согласуется с тем, как ведет себя указатель указателя. Это означает, что p1 + (p2 - p1) == p2 *.

Добавление указателя (добавление целого к указателю) ведет себя аналогичным образом: p1 + 1 дает вам адрес следующего элемента в массиве, а не следующий байт в массиве - это было бы довольно бесполезно и небезопасно.

C мог быть спроектирован так, чтобы указатели добавлялись и вычитались так же, как целые числа, но это означало бы:

  • делает p2 = p1 + n * sizeof(*p1) вместо p2 = p1 + n
  • , делая n = (p2 - p1) / sizeof(*p1) вместо n = p2 - p1

Это приведет к тому, что код

*: предполагается, что разница между p1 и p2 является точным кратным типа, на который они указывают. Если они находятся в одном массиве памяти, это будет верно. Если это не так, то это не имеет большого значения, делая арифметику указателей на них.

1
ответ дан mwfearnley 5 September 2018 в 07:36
поделиться

«Когда вы вычитаете два указателя, пока они указывают на один и тот же массив, результатом будет количество элементов, разделяющих их»

Проверьте еще здесь .

3
ответ дан Praveen S 5 September 2018 в 07:36
поделиться

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

9
ответ дан ptomato 5 September 2018 в 07:36
поделиться

@fahad Указатель арифметики проходит по размеру типа данных, который он указывает. Поэтому, когда указатель ur имеет тип int, вы должны ожидать арифметики указателя в размере int (4 байта). Аналогично для указателя char все операции над указателем будет в 1 байт.

0
ответ дан The Stig 5 September 2018 в 07:36
поделиться
Другие вопросы по тегам:

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