Как работают обещания и цепочка обещаний? [Проблема с кодом]

Я изучаю Node.js и пытаюсь правильно использовать модуль mysql2. Из-за этого я недавно начал исследовать промисы.

Я пишу что-то вроде «библиотеки», поэтому я могу практиковаться во всех этих темах, и при этом я столкнулся с проблемой с цепочкой обещаний, которую я не могу понять. Любая помощь приветствуется!

Проблема в следующем:

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

Теперь, если я напишу свою функцию query следующим образом:


query(){
        let p = new Promise((resolve, reject) => {
            resolve("Hello world")
        });


        p.then(data => {
            console.info("Hello world a second time!");
        }).then(data => {
            console.info("Hello world a third time")
        })
        return p;
    }

и я пытаюсь «использовать» это обещание из другого файла следующим образом:


DBObject.query().then((data) => {
    console.info("Hello world from the other file!");
})

Тогда вывод в неправильном порядке, программа печатает это:

Hello world a second time!
Hello world from the other file!
Hello world a third time


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

query(){
        let p = new Promise((resolve, reject) => {
            resolve("Hello world")
        }).then(data => {
            console.info("Hello world a second time!");
        }).then(data => {
            console.info("Hello world a third time")
        })

        return p;
    }

Он отлично работает и печатает:

Hello world a second time!
Hello world a third time
Hello world from the other file!

Я не понимаю такого поведения, я думал, что объявление блоков then отдельно от определения промиса будет таким же, как и создание цепочки промисов сразу после объявления промиса, а это явно не так!

Заранее спасибо за ответы, которые вы можете мне дать. Кроме того, было бы здорово, если бы вы могли дать мне несколько советов о том, как правильно писать такой код. Я имею в виду, если я пишу код, использующий промисы, что я должен вернуть пользователю? Еще одно обещание? Или просто данные для работы? Мне бы очень хотелось писать код, который следует «стандартному» способу ведения дел.

Приветствую вас всех! Еще раз спасибо.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
8
0
200
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вместо возврата p в отдельной версии... верните цепочку p.then(). Последний будет добавлен в конец этой цепочки, а не создавать две разные цепочки.

then() возвращает новое обещание, разрешенное любым возвратом или undefined, если возврата нет

const query = () => {
  let p = new Promise((resolve, reject) => {
    resolve("Hello world")
  });

  return p.then(data => {
    // from `resolve()`
    console.info('Then 1: ', data)
    // return to next then()
    return ("Hello world a second time!");
  }).then(data => {
    // from previous then()
    console.info('Then 2: ', data)
    // return to next then()
    return ("Hello world a third time")
  });
}


query().then(res => console.info('Final: ', res))

Спасибо за ответ, chaflietfl! Я понятия не имел, что могу вернуть обещание, вернув p.then. Это, в сочетании с ответом CertainPerformance, действительно очень помогло! Я протестировал ваш код внутри своей функции, и он отлично сработал, мне понравился этот подход.

x_x 09.04.2019 00:24

Как только вы поймете, что каждый then — это новое обещание, его будет легче визуализировать.

charlietfl 09.04.2019 00:27
Ответ принят как подходящий

Когда у вас есть одно обещание, вы можете связать любой номер обещаний с его .then. Например

const p = Promise.resolve();
p.then(() => console.info('then 1');
p.then(() => console.info('then 2');

означает, что p имеет два Promises, которые от него ответвляются, когда он разрешается: 1 и 2 (в дополнение к самому обещанию p).

  p
 / \
/   \
1   2

Что вы делаете в своем первом коде

let p = new Promise((resolve, reject) => {
  resolve("Hello world")
});
p.then(data => {
  console.info("second");
}).then(data => {
  console.info("third")
})
return p;

как

"Hello world" = <Promise you return>
    |
    |
    |
  second
    |
    |
    |
  third = <unused Promise expression that the then chain resolves to>

У вас есть две ветви: обещание, которое вы возвращаете, разрешается, когда запускается Hello world, а не когда запускается third.

С другой стороны, когда вы вызываете .then несколько раз для обещания, все выражение оценивается как обещание, которое разрешает когда бежит финал .then:

let p = new Promise((resolve, reject) => {
  resolve("Hello world")
}).then(data => {
  console.info("Hello world a second time!");
}).then(data => {
  console.info("Hello world a third time")
})

return p;

как

"Hello world"
     |
     |
'Hello second'
     |
     |
'Hello third' = <Promise you return>

где возвращенный промис — это тот, который разрешается сразу после запуска Hello third.

Спасибо за отличное и подробное объяснение, CertainPerformance! Теперь это имеет смысл, и это здорово, потому что я определенно был сбит с толку. :)

x_x 09.04.2019 00:20

Верно ли также, что если вы возвращаете промис внутри onResolveCallback из .then(onResolveCallback), это промис не тот, который вы используете в цепочке. Обещание, возвращаемое .then(), является другим обещанием, которое разрешается только после выполнения onResolveCallback, и, если возвращаемое значение onResolveCallback также является обещанием, остается в состоянии ожидания до тех пор, пока обещание, возвращенное onResolveCallback, тоже не разрешается? Это правильно? Надеюсь, я был понятен.

tonix 21.04.2019 15:16

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