С Новым годом! Форум программистов, компьютерный форум, киберфорум
JavaScript
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.88/26: Рейтинг темы: голосов - 26, средняя оценка - 4.88
86 / 13 / 1
Регистрация: 24.05.2010
Сообщений: 590

Promise: а где асинхронность?

18.04.2019, 15:30. Показов 5150. Ответов 14
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Привет!

Согласно https://developer.mozilla.org/... s/Promise: Объект Promise (обещание) используется для отложенных и асинхронных вычислений. Promise может находиться в трёх состояниях

Выполняю такой код:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    'use strict';
 
    function pause()
    {
        var sum = 0;        
        for(var i=0; i <= 1000000000; i++) {
            sum += i * 3 / 4;
            sum = sum / 2;
        }
    }
    
    console.log("start: " + new Date().getTime());  
    
    let promise = new Promise((resolve, reject) => {
        console.log("promise start " + new Date().getTime());
        pause();
        console.log("promise finish " + new Date().getTime());
    });
    
    promise
      .then(
        result => {
          console.log("Fulfilled: " + result);
        },
        error => {
          console.log("Rejected: " + error);
        }
      );
      
      console.log("end: " + new Date().getTime());
В консоли выводится:

start: 1555590386022
promise start 1555590386022
promise finish 1555590388627
end: 1555590388627

Вопрос: а где тут асинхронность?
1
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
18.04.2019, 15:30
Ответы с готовыми решениями:

Выход из Promise
Всем привет! Есть такая конструкция: function ajax_api(method, date) { return new Promise(function (succeed, fail) { xhr =...

Uncaught (in promise) DOMException Audio
Добрый, Собственно проблема, Загружаю трек let backgroundMusic = new Audio(); backgroundMusic.src =...

Асинхронность javascript
Не уверен, является ли то, о чём хочу спросить, асинхронностью. И может я чего-то не понимаю. Но надеюсь на вашу помощь. Пример: есть...

14
Эксперт JS
2463 / 1769 / 625
Регистрация: 11.07.2016
Сообщений: 4,067
18.04.2019, 16:35
Всё потому, что в ваш промис передана функция, не содержащая вообще никакого асинхронного кода. А значит использование промиса в данном случае попросту бессмысленно.
Добавим немного асинхронности:
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    'use strict';
    
    console.log("start: " + new Date().getTime());  
    
    let promise = new Promise((resolve, reject) => {
        console.log("promise start " + new Date().getTime());
        setTimeout(() => resolve(new Date().getTime()), 3000);
        console.log("promise finish " + new Date().getTime());
    });
    
    promise
      .then(
        result => {
          console.log("Fulfilled: " + result);
        },
        error => {
          console.log("Rejected: " + error);
        }
      );
      
      console.log("end: " + new Date().getTime());
Добавлено через 3 минуты
Более того в вашем коде вы не разрешаете промис вызовом функций resolve() или reject(), а значит обработка никогда не дойдёт до обработчика внутри promise.then()
1
86 / 13 / 1
Регистрация: 24.05.2010
Сообщений: 590
18.04.2019, 16:43  [ТС]
Balanaar, а зачем же тогда в принципе использовать Promise, если код у нас до него был асинхронный? Для лучшей читаемости?
0
Эксперт JS
2463 / 1769 / 625
Регистрация: 11.07.2016
Сообщений: 4,067
18.04.2019, 16:56
Всё просто.
При программировании на JS вы можете столкнуться с какими-либо асинхронными функциями. Вам нужно выполнить их, получить результат и только потом работать с полученным результатом.
Например, вы осуществляете AJAX-запрос на сервер. Для того, чтобы он выполнился, нужно некоторое время: сформировать запрос, отправить запрос, обработать на сервере и получить ответ. Нельзя сказать наверняка, сколько времени это займёт. В теории можно отправить синхронный запрос на сервер и остановить исполнение скрипта, пока мы не дождёмся ответа, но это не есть хорошо, потому что на время исполнения запроса страница подвиснет.
Сымитируем AJAX-запрос на сервер с помощью асинхронной функции setTimeout(). В качестве времени задержки мы передадим случайное число от 1 до 5 секунд. Мы не знаем, сколько по времени будет отрабатывать setTimeout(), поэтому используем промис, чтобы работать с результатом сразу после получения ответа:
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
const ready = new Promise((resolve, reject) => {
  console.log('Отправляем запрос на сервер');
  setTimeout(() => {
    console.log('Ответ получен');
    resolve('ready');
  }, Math.floor(Math.random() * 4000) + 1000);
});
 
ready.then((result) => {
  console.log('Работаем с результатом: ' + result);
});
 
console.log('Пока ждём ответ, делаем другие вещи');
1
86 / 13 / 1
Регистрация: 24.05.2010
Сообщений: 590
18.04.2019, 17:09  [ТС]
Balanaar, я не могу понять почему просто не сделать так:

JavaScript
1
2
3
4
5
6
7
    console.log('Отправляем запрос на сервер');
    setTimeout(() => {
        console.log('Ответ получен');
        console.log('Работаем с результатом: ');
      }, Math.floor(Math.random() * 4000) + 1000);
      
    console.log('Пока ждём ответ, делаем другие вещи');
?
0
Эксперт JS
2463 / 1769 / 625
Регистрация: 11.07.2016
Сообщений: 4,067
18.04.2019, 17:15
V0fka, можно и так. Только это то, что называется callback hell (дословно: ад обратных вызовов).
В более-менее сложных проектах можно столкнуться с чередой асинхронных функций. В таком случае придётся вкладывать каждую следующую функцию в коллбек предыдущей и получить уровень вложенности до 100 и даже больше.
Промисы решают эту проблему. К тому же это удобный инструмент для отлова ошибок благодаря состоянию rejected промиса.
1
86 / 13 / 1
Регистрация: 24.05.2010
Сообщений: 590
18.04.2019, 17:26  [ТС]
Balanaar, ну то есть это не что-то новое по своей природе (я думал, что это штука, которая асинхронно выполняет синхронный код), а просто, так сказать "синтаксический сахар", созданный для удобства разработки?
0
Эксперт JS
2463 / 1769 / 625
Регистрация: 11.07.2016
Сообщений: 4,067
18.04.2019, 17:46
Лучший ответ Сообщение было отмечено V0fka как решение

Решение

Ещё одна удобная фишка промисов - это отслеживание множества асинхронных функций и выполнение кода только после исполнения их всех. Например, вам нужно выполнить какую-нибудь обработку только после того, как вы загрузили 50 изображений:
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
const images = [];
for (let i = 1; i <= 50; i++) {
  const img = new Image();
  img.src = `/images/${i}.jpg`;
  images.push(img);
  img.ready = new Promise((resolve, reject) => {
    img.onload = (() => resolve());
  });
}
 
Promise.all(images.map((img) => img.ready)).then(() => {
  console.log('Все изображения загружены. Работаем с ними.');
})
Добавлено через 1 минуту
Цитата Сообщение от V0fka Посмотреть сообщение
а просто, так сказать "синтаксический сахар"
Можно сказать и так.

Добавлено через 16 минут
Цитата Сообщение от V0fka Посмотреть сообщение
я думал, что это штука, которая асинхронно выполняет синхронный код
Можно поместить ваш синхронный код в асинхронную функцию:
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    'use strict';
 
    function pause()
    {
        var sum = 0;        
        for(var i=0; i <= 1000000000; i++) {
            sum += i * 3 / 4;
            sum = sum / 2;
        }
        return sum;
    }
    
    console.log("start: " + new Date().getTime());  
    
    setTimeout(() => {
        console.log(pause());
    }, 0)
      
    console.log("end: " + new Date().getTime());
1
86 / 13 / 1
Регистрация: 24.05.2010
Сообщений: 590
18.04.2019, 17:47  [ТС]
Balanaar, ясно, спасибо за ответы!
0
Эксперт JS
6496 / 3907 / 2006
Регистрация: 14.06.2018
Сообщений: 6,781
18.04.2019, 20:42
Лучший ответ Сообщение было отмечено amr-now как решение

Решение

V0fka, ответ на Ваш вопрос:
В скрипте была ошибка. Промис никогда не получал статус завершенного. Поэтому метод then() никогда не выполнялся. А в данном скрипте именно then() выполняется асинхронно.


Вот правильный скрипт для технологии then() :
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
        function pause() {
            var sum = 0;
            for (var i = 0; i <= 1000000000; i++) {
                sum += i * 3 / 4;
                sum = sum / 2;
            }
        }
 
        console.log("start: " + new Date().getTime());
 
        let promise = new Promise((resolve, reject) => {
            console.log("promise start " + new Date().getTime());
            pause();
            console.log("promise finish " + new Date().getTime());
            resolve(777);
        });
 
        promise
            .then(
                result => {
                    console.log("Fulfilled: " + result);
                },
                error => {
                    console.log("Rejected: " + error);
                }
            );
 
        console.log("end: " + new Date().getTime());
Добавлено через 4 минуты
-----
Хотя да. По логике настоящей асинхронности инструкция console.log("end: " + new Date().getTime()); должна была выполниться сразу же после старта.
Имхо, такая мелкая особенность реализации в JavaScript.

Добавлено через 2 минуты
Видимо, пока с then() скрипт ковырялся, он решил, что можно и "рабочий код" промиса запустить

Добавлено через 6 минут
--------
Вот логически более корректный вызов "рабочего кода", где callback - "рабочий код":
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
        Promise.run = callback => new Promise((resolve, reject) => {
            try {
                let result = callback();
                resolve(result);
            }
            catch (e) {
                reject(e);
            }
        });
        /**
         * Задержка в миллисекундах
         */
        Promise.delay = ms => new Promise(resolve => { setTimeout(resolve, ms); });
1
Эксперт JS
6496 / 3907 / 2006
Регистрация: 14.06.2018
Сообщений: 6,781
20.04.2019, 17:47
Лучший ответ Сообщение было отмечено amr-now как решение

Решение

Несколько источников в интернете сообщили, что согласно спецификации функция обратного вызова выполняется в конструкторе синхронно, то есть без ожидания.
https://www.ecma-international... onstructor
When the Promise function is called with argument executor, the following steps are taken:
1. If NewTarget is undefined, throw a TypeError exception.
2. If IsCallable(executor) is false, throw a TypeError exception.
3. Let promise be ? OrdinaryCreateFromConstructor(NewTarget, "%PromisePrototype%", « [[PromiseState]], [[PromiseResult]], [[PromiseFulfillReactions]], [[PromiseRejectReactions]], [[PromiseIsHandled]] »).
4. Set promise.[[PromiseState]] to "pending".
5. Set promise.[[PromiseFulfillReactions]] to a new empty List.
6. Set promise.[[PromiseRejectReactions]] to a new empty List.
7. Set promise.[[PromiseIsHandled]] to false.
8. Let resolvingFunctions be CreateResolvingFunctions(promise).
9. Let completion be Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »).
10. If completion is an abrupt completion, then
Perform ? Call(resolvingFunctions.[[Reject]], undefined, « completion.[[Value]] »).
11. Return promise.
То есть, пока функция executor не выполнится, промис даже не будет возвращён.
Да. Это особенность реализации промисов в JavaScript, отличающаяся от эталонного асинхронного поведения.

Добавлено через 12 минут
Причём что интересно, на сайте MDN о данной особенности промисов не упоминается, как и в онлайн-учебнике http://learn.javascript.ru
То есть вот здесь ошибка:
Способ использования, в общих чертах, такой:
Код, которому надо сделать что-то асинхронно, создаёт объект promise и возвращает его.
Внешний код, получив promise, навешивает на него обработчики.
По завершении процесса асинхронный код переводит promise в состояние fulfilled (с результатом) или rejected (с ошибкой). При этом автоматически вызываются соответствующие обработчики во внешнем коде.
Пока executor не завершится, никакого промиса возвращено НЕ будет, даже в состоянии pending.
2
Эксперт JS
6496 / 3907 / 2006
Регистрация: 14.06.2018
Сообщений: 6,781
21.04.2019, 10:37
Вот исправление для "правильной" асинхронности промисов в браузере:
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
        /*
        * Статический метод, обеспечивающий механизм "правильной" асинхронности с обработкой ошибок.
        * Действительно сначала возвращает промис в состоянии ожидания,
        * и действительно запускает функцию обратного вызова на следующем цикле Event Loop.
        */
        Promise.run = callback => new Promise((resolve, reject) => {
            setTimeout(() => {
                try {
                    let result = callback();
                    resolve(result);
                }
                catch (e) {
                    reject(e);
                }
            }, 0);
        });
 
        function pause() { // Некий очень долго выполняющийся участок кода.
            let sum = 0;
            for (var i = 0; i <= 1000000000; i++) {
                sum += i * 3 / 4;
                sum = sum / 2;
            }
        }
 
        // Точка входа в скрипт
        (function main() {
            console.log("start: " + new Date().getTime());
            work();
            console.log("end: " + new Date().getTime());
        })();
 
        async function work() {
            try {
                // Механизм "правильной" асинхронности с обработкой ошибок
                let result = await Promise.run(workCode);
 
                console.log("Fulfilled: " + result);
            }
            catch (error) {
                console.log("Rejected: " + error);
            }
        }
 
        /*
        * "Рабочий код" - произвольная функция типа Func<T>, 
        * не принимающая параметры и возвращающая значение любого типа или не возвращающая ничего.
        * Поскольку не принимает параметры, 
        * то в Promise.run() обычно оборачивается в стрелочную функцию с переменными-замыканиями.
        */
        function workCode() {
            console.log("promise start " + new Date().getTime());
            pause();
            console.log("promise finish " + new Date().getTime());
            return 777;
        }
1
86 / 13 / 1
Регистрация: 24.05.2010
Сообщений: 590
22.04.2019, 09:51  [ТС]
amr-now, такой код:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
    function pause() {
            var sum = 0;
            for (var i = 0; i <= 1000000000; i++) {
                sum += i * 3 / 4;
                sum = sum / 2;
            }
        }
 
        console.log("start: " + new Date().getTime());
 
        let promise = new Promise((resolve, reject) => {
            console.log("promise start " + new Date().getTime());
            //pause();
            console.log("promise finish " + new Date().getTime());
            resolve(777);
        });
 
        promise
            .then(
                result => {
                    pause();
                    console.log("Pause in then Fulfilled: " + result);
                },
                error => {
                    pause();
                    console.log("Pause in then Rejected: " + error);
                }
            );
 
        pause();
        console.log("pause1 " + new Date().getTime());
 
        pause();
        console.log("pause2 " + new Date().getTime());
        
        pause();
        console.log("pause3 " + new Date().getTime());
 
        console.log("end: " + new Date().getTime());
у меня стабильно выводит

start: 1555915391554
promise start 1555915391555
promise finish 1555915391555
pause1 1555915394131
pause2 1555915397024
pause3 1555915399899
end: 1555915399899
Pause in then Fulfilled: 777
То есть то что в then выполняется синхронно, причем после того, как основной поток выполнения завершится.

То что вы написали в сообщении №12 я пока не сильно вкурил . То что там написано, это именно асинхронный код (судя по async/await), но обязательно ли в подобном асинхронном коде присутствие промисов или же это не обязательное условие написания асинхронного JS кода?
0
Эксперт JS
6496 / 3907 / 2006
Регистрация: 14.06.2018
Сообщений: 6,781
22.04.2019, 15:12
Лучший ответ Сообщение было отмечено V0fka как решение

Решение

V0fka, последовательность действий:
1) стартуем.
2) выполняем executor промиса - вывелись все надписи в коллбэке. Промис получил статус resolved.
3) Вернули промис в переменную. Результат промиса пока никому не интересен. Едем дальше.
4) отработали 3 паузы с выводом соответствующих надписей.
5) вывели "end". Всё. Текущий "рабочий код" закончился.
6) Смотрим в очередь ожидания в Event Loop.

7) Увидели что в очереди ожидания в Event Loop болтается "рабочий код" метода then(), который как раз соответствует промису в состоянии "Выполнен с любым результатом". В данном случае успешно.
8) Именно асинхронно в классическом понимании выполняем then() для результата 777.

---------------
Народ ещё подсказывает, что setImmediate() реализован только в Microsoft и NodeJS.
Поэтому асинхронность конструктора промиса пока пришлось закостылить методом setTimeout(callback, 0)

Но есть более хитрые способы слегка уменьшить разрывы между циклами Event Loop.
Например:
https://learn.javascript.ru/setimmediate
https://habr.com/ru/post/275023/
Имхо, для достаточно продолжительного "рабочего кода" потери на разрывы Event Loop не будут настолько существенными, как кажутся. setImmediate() всё равно круче ))

Добавлено через 3 часа 47 минут
Да. Можно использовать фиктивный положительно завершенный промис, а функцию обратного вызова запускать в служебном then():
PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
</head>
<body>
    <script>
        /*
        * Статический метод, обеспечивающий механизм "правильной" асинхронности с обработкой ошибок.
        * Действительно сначала возвращает промис в состоянии ожидания,
        * и действительно запускает функцию обратного вызова в асинхронном режиме.
        */
        Promise.run = callback => new Promise((resolve, reject) => {
            Promise.resolve().then(() => {
                try {
                    let result = callback();
                    resolve(result);
                }
                catch (e) {
                    reject(e);
                }
            });
        });
 
        function pause() { // Некий очень долго выполняющийся участок кода.
            let sum = 0;
            for (var i = 0; i <= 1000000000; i++) {
                sum += i * 3 / 4;
                sum = sum / 2;
            }
        }
 
        // Точка входа в скрипт
        (function main() {
            console.log("start: " + new Date().getTime());
            work();
            console.log("end: " + new Date().getTime());
        })();
 
        async function work() {
            try {
                // Механизм "правильной" асинхронности с обработкой ошибок
                let result = await Promise.run(workCode);
 
                console.log("Fulfilled: " + result);
            }
            catch (error) {
                console.log("Rejected: " + error);
            }
        }
 
        /*
        * "Рабочий код" - произвольная функция типа Func<T>, 
        * не принимающая параметры и возвращающая значение любого типа или не возвращающая ничего.
        * Поскольку не принимает параметры, 
        * то в Promise.run() обычно оборачивается в стрелочную функцию с переменными-замыканиями.
        */
        function workCode() {
            console.log("promise start " + new Date().getTime());
            pause();
            console.log("promise finish " + new Date().getTime());
            return 777;
        }
    </script>
</body>
</html>
2
86 / 13 / 1
Регистрация: 24.05.2010
Сообщений: 590
24.04.2019, 09:03  [ТС]
amr-now, спасибо за ответы! То что вы пишете это уже слишком продвинутый уровень JavaScript для меня. Сходу не сильно все понятно, постараюсь на досуге осознать все написанное. Еще раз спасибо!
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
24.04.2019, 09:03
Помогаю со студенческими работами здесь

SetTimeout, clearTimeout - асинхронность, однопоточность
Как это работает? почему, запуская множество setTimeout, внутри setInterval, фиксируя остановку SetTimeout с помощью if, а setInterval...

Напишите функцию, поведение которой аналогично поведению Promise.race(promises)
Напишите функцию promiseRace(promises), поведение которой аналогично поведению Promise.race(promises). @param {Promise} promises...

Promise, как дождаться выполнения
Здравствуйте. Есть код, у меня вместо setTimeout идет запись в indexedDB, но это не должно менять суть вещей. console.clear(); ...

Исправить порядок выполнения promise
function serverTalk () { var myInnerServerTalk = new Promise ( function (resolve,reject) { do { var zn...

Асинхронность в js
почему в консоле не находит переменную obj, но когда убираю setTimout, только тогда находит async function regUser() { ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Новые блоги и статьи
Изучаю kubernetes
lagorue 13.01.2026
А пригодятся-ли мне знания kubernetes в России?
Сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
Модель микоризы: классовый агентный подход 3
anaschu 06.01.2026
aa0a7f55b50dd51c5ec569d2d10c54f6/ O1rJuneU_ls https:/ / vkvideo. ru/ video-115721503_456239114
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR
ФедосеевПавел 06.01.2026
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR ВВЕДЕНИЕ Введу сокращения: аналоговый ПИД — ПИД регулятор с управляющим выходом в виде числа в диапазоне от 0% до. . .
Модель микоризы: классовый агентный подход 2
anaschu 06.01.2026
репозиторий https:/ / github. com/ shumilovas/ fungi ветка по-частям. коммит Create переделка под биомассу. txt вход sc, но sm считается внутри мицелия. кстати, обьем тоже должен там считаться. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru