0

Почему .then не взаимодействует со внешними переменными: при попытке переопределить переменную внутри .then, внутри самого .then она переопределяется, но вне нет.

const promise = new Promise((resolve) => {
  resolve(3);
});

let a;

promise.then(val => { a = val; console.log(a) });

console.log(a)

Пытался сделать обходной путь - передавать в .then функцию, которая переопределяет переменную a, но безуспешно:

const promise = new Promise((resolve) => {
  resolve(3);
});

let a;

function toRes(val) { a = val; }

promise.then(toRes);

console.log(a)


const a = {
  get rates() {
    fetch('https://blockchain.info/ru/ticker')
      .then(rates => {
        return rates;
      });
  }
}

console.log(a.rates);




let res;
fetch('https://blockchain.info/ru/ticker')
  .then(rate => {
    res = rate;
    console.log(res);
  });
setTimeout(console.log, 1000, res);
kertAW
  • 2,443
  • 4
    Потому что никто не обещал, что then отработает раньше чем нижний console.log. Это ж асинхронщина – andreymal Nov 01 '20 at 00:23
  • 2
    Собственно, в ECMA-262 про then так и прописано: если промис fulfilled, то поставить выполнение функции в очередь. А дело до этой очереди дойдёт сильно потом, после выполнения всего остального кода, в том числе после нижнего console.log – andreymal Nov 01 '20 at 00:33
  • @andreymal, как можно дождаться выполнения .then? Я пробовал пихать его в асинхронную IIFE, но бестолку. – kertAW Nov 01 '20 at 00:38
  • Зачем его дожидаться? Всё, что нужно выполнить при выполнении then, пишите внутри этого самого then, вот и всё – andreymal Nov 01 '20 at 00:39
  • @andreymal, я обновил вопрос, добавил туда свой пример. В данном случае тоже undefined. – kertAW Nov 01 '20 at 00:44
  • Геттер rates ничего не возвращает, потому и undefined – andreymal Nov 01 '20 at 00:45
  • Как тогда можно вернуть в геттер rates результат промиса? – kertAW Nov 01 '20 at 00:48
  • Никак. Да и зачем? Нужен результат из промиса — пишите код в then, вот и всё – andreymal Nov 01 '20 at 00:50
  • Мне нужно, чтобы rates был равен результату промиса. – kertAW Nov 01 '20 at 00:51
  • 1
    Это технически невозможно. – andreymal Nov 01 '20 at 00:52
  • Ладно, на самом деле это возможно, если вместо fetch вы сделаете блокирующий запрос через XMLHttpRequest (который остался в браузерах по историческим причинам), тем самым вызвав подвисание страницы на время работы запроса. Но если вы так сделаете, вы заработаете дурную славу в js-сообществе, да и сам браузер в консоли предупредит, что так делать не надо – andreymal Nov 01 '20 at 00:54
  • @andreymal, а возможно ли вообще передать результат промиса за сам промис? – kertAW Nov 01 '20 at 00:56
  • 1
    Конечно возможно, первый пример в вашем коде именно это и делает. Просто вы вызываете нижний console.log слишком рано; если его хотя бы завернуть в банальный setTimeout, чтобы таким образом дождаться выполнения then, то undefined заменится на желанное 3 – andreymal Nov 01 '20 at 00:57
  • Если вам религия запрещает писать код внутри then, вы можете завернуть всё своё в асинхронную функцию и дожидаться выполнения промисов с помощью await: https://jsfiddle.net/dfhqex47/1/ Учебник: https://learn.javascript.ru/async-await – andreymal Nov 01 '20 at 01:02
  • @andreymal, все равно ничего не работает, я обновил вопрос. – kertAW Nov 01 '20 at 01:19
  • 1
    Потому что на момент вызова setTimeout значение res было undefined, вы по-прежнему не дождались промиса перед обращением к res, вот именно undefined и отпечаталось – andreymal Nov 01 '20 at 01:25
  • 1
    setTimeout(() => console.log(res), 1000) – andreymal Nov 01 '20 at 01:25
  • @Voprositel Так а что мешает сделать console.log(await fetch('https://blockchain.info/ru/ticker')) ? – Геннадий П Nov 01 '20 at 10:35

1 Answers1

4

Я бы посоветовал вот так пользоваться промисами:
Когда вы подписываетесь на изменения .then, то при срабатывании просто вызываете функцию, которая выполнит ваш код

const promise = new Promise((resolve) => {
  resolve(3);
})
.then(res => {
  promiseWorked(res);
});

function promiseWorked(res) { console.log(res); }

Если вы хотите писать код сверху вниз, можете использовать async/await:

void async function() {
  const promise = new Promise((resolve) => {
    resolve(3);
  });
  const promiseRes = await promise;
  console.log(promiseRes);
}();

НО, пока не выполнится await, следующая строчка кода не выполнится в async функции, и если сработает ошибка, то мы это не отловим, а также весь код ниже await не выполнится:

void async function() {
  const promise = new Promise((resolve, reject) => {
    reject(3);
  });
  const promiseRes = await promise;
  console.log(promiseRes);
  console.log(11111);
}();

Поэтому, если вы используете await, необходимо пользоваться конструкцией try/catch, чтобы ловить ошибки:

void async function() {
  const promise = new Promise((resolve, reject) => {
    reject(3);
  });
  try {
    const promiseRes = await promise;
    console.log(promiseRes); // Если у промиса сработал resolve, то сработает эта строчка, но мы вызываем reject
  } catch (e) {
    console.error(e);
  }
}();