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

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

18.04.2019, 15:30. Показов 5168. Ответов 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
Ответ Создать тему
Новые блоги и статьи
Хочу заставить корпорации вкладываться в здоровье сотрудников: делаю мат модель здравосохранения
anaschu 22.03.2026
e7EYtONaj8Y Z4Tv2zpXVVo https:/ / github. com/ shumilovas/ med2. git
1С: Программный отбор элементов справочника по группе
Maks 22.03.2026
Установка программного отбора элементов справочника "Номенклатура" из модуля формы документа. В качестве фильтра для отбора справочника служит группа номенклатуры. Отбор по наименованию группы. . .
Как я обхитрил таблицу Word
Alexander-7 21.03.2026
Когда мигает курсор у внешнего края таблицы, и нам надо перейти на новую строку, а при нажатии Enter создается новый ряд таблицы с ячейками, то мы вместо нервных нажатий Энтеров мы пишем любые буквы. . .
Krabik - рыболовный бот для WoW 3.3.5a
AmbA 21.03.2026
без регистрации и смс. Это не торговля, приложение не содержит рекламы. Выполняет свою непосредственную задачу - автоматизацию рыбалки в WoW - и ничего более. Однако если админы будут против -. . .
1С: Программный отбор элементов справочника по значению перечисления
Maks 21.03.2026
Установка программного отбора элементов справочника "Сотрудники" из модуля формы документа. В качестве фильтра для отбора служит значение перечислений. / / Событие "НачалоВыбора" реквизита на форме. . .
Переходник USB-CAN-GPIO
Eddy_Em 20.03.2026
Достаточно давно на работе возникла необходимость в переходнике CAN-USB с гальваноразвязкой, оный и был разработан. Однако, все меня терзала совесть, что аж 48-ногий МК используется так тупо: просто. . .
Оттенки серого
Argus19 18.03.2026
Оттенки серого Нашёл в интернете 3 прекрасных модуля: Модуль класса открытия диалога открытия/ сохранения файла на Win32 API; Модуль класса быстрого перекодирования цветного изображения в оттенки. . .
SDL3 для Desktop (MinGW): Рисуем цветные прямоугольники с помощью рисовальщика SDL3 на Си и C++
8Observer8 17.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-rectangles-sdl3-c. zip finish-rectangles-sdl3-cpp. zip
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru