Репутація українського ІТ. Пройти опитування Асоціації IT Ukraine
×Закрыть

Питання стосовно використання Promise в Node.js

Коли проміс створюється з оператором new, то з подальшим використанням функцій resolve, reject питань не виникає.

let promise = new Promise( (resolve, reject) =>
{
  let result = ...
  if( result === 'ok' ) resolve(result);
 ...
  if(err) reject(err);
} )

Питання виникають коли доцільно використовувати методи Promise.resolve(), Promise.reject(). З документації зрозуміло, що їх можна використовувати, наприклад, щоб повернути певне значення загорнуте у промісі:

Promise.resolve("Success")
.then( value =>
{
  console.log(value); // "Success"
});

Такий варіант буде корисним, бо він дозволяє у скороченій формі повернути результат у вигляді проміса, якійсь функції, яка очікує саме промісу...

Але чи доречно ці методи використовувати десь всередині колбеку, наприклад так:

promise
.then( () =>
{
  request.dateRegistered = Date.now();

  // Припустимо тут дуже багато подібних простих операцій (без звернення до зовнішніх БД, зовнішніх систем і т.д.)
  // ...

  return Promise.resolve();
} )
.then( () =>
{
  console.log('this is next code');
})

В такому разі наступний then, буде очікувати поки зарезолвиться попередній then, чи ні? Мої експеременти показують, що спочатку виконується код у першому, а тоді у другому колбеках для then. Але припускаю, що може на практиці таки колись виконається спочатку другий, а потім лише перший код.

Хтось обкатував такий код в продакті? Як він себе поводить?

LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Почему promise API несимметричен относительно деления на первичные функции (в new) и вторичные (во then/catch)?

В первичных надо получать аргументами resolve и reject, и вызывать их явно (проверил на promise 7.1.1 для nodejs — простой return со значением игнорируется, как будто промис незавершён).
Во вторичных надо возвращать значение или генерировать исключение, параметров resolve, reject нет.

Я бы понял логику, или что
1) у всех функций-коллбэков возврат значения работает как resolve с этим значением, а генерация исключения — как reject,
или что
2) все функции-коллбэки имеют три входных параметра — input, resolve, reject,

а лучше — и то, и другое (можно вернуть значение в конце, а можно — вернуть исключение и выйти; а ещё лучше — предусмотреть специальный тип исключения для resolve).

Также нет возможности написать <promise object>.resolve(значение), и аналогично для reject — тоже было бы значительно удобнее (и извне, как уже обсуждают, и изнутри). (Тогда можно было бы вторым параметром передавать сам объект промиса, для вызовов его методов.)

Или я не вижу каких-то хитростей, которые решают это, и которые можно найти только с заметным опытом их использования?

Коли ми напряму викликаємо new Promise, ми створюємо певний об’єкт промісу, а коли ми викликаємо Promise.resolve() чи Promise.reject(), - ми також опосередковано викликаємо конструктор new Promise, але кожен із них створює вже окремий об’єкт, і саме через це ми не можемо резолвити той проміс, в методах якого ми зробимо Promise.resolve():

let promise = getPromise(); // створюємо проміс від якоїсь там зовнішньої функції

// Працюємо із цим промісом
promise
.then( () =>
{
  Promise.resolve('some value');
})
.then( result =>
{
  console.log(result)   // undefind
})

В межах першого метода then() ніякого резолва не буде, бо Promise.resolve(’some value’) просто створює новий об’єкт, і він немає доступу до зовнішнього об’єкта, який було створено на початку цього виразу.

В той час, коли ми пишемо так:

let promise = new Promise( (resolve, reject) =>
{
  .....
  if(err) return reject(err);
  
  resolve();
})

через колбеки resolve та reject ми управляємо зовнішнім промісом, бо він сам делегує таке управління.

Все це ніяк не відповідає на мої запитання.

Почему promise API несимметричен относительно деления на первичные функции (в new) и вторичные (во then/catch)?

Відповідь: тому що при resolve/reject ми вирішуємо проміс, а при then/catch ми ловимо результат цього промісу.

І таким же чином створюємо нове значення, яке може піти далі за ланкою промісів. Тому — не аргумент.

Коли вже зовнішній проміс є вирішеним, то лише самий перший then ловить його результат, а тому не логічно, щоб в рамках цього першого then ще щось додатково викликало resolve() чи reject().

Якщо ви далі хочете продовжити ланцюжок промісів, то ви повинні самі створити Promise в рамках першого then, результат якого буде ловити вже другий then і т.д.

Якщо ви далі хочете продовжити ланцюжок промісів, то ви повинні самі створити Promise в рамках першого then

then створює новий проміс.

1. then ловить результат, попереднього промісу;
2. then повертає результат, переданого йому колбеку, у вигляді промісу;
2.1. якщо в рамках then, ви не повертаєте жодного результату через retun, то наступний then не спіймає ніякого результату, окрім пустого промісу;
2.2. якщо в рамках then, ви повертаєте новостворений проміс, то наступний then буде викликатись після вирішення цього проміса і отримає його результат.

Таким чином, виклик resolve/reject в рамках then ніяким чином не може вирішити попередній проміс, бо він ловить завжди вирішений проміс, хоча й сам може створити черговий проміс.

Таким чином, виклик resolve/reject в рамках then ніяким чином не може вирішити попередній проміс

Я такого і не казав.

хоча й сам може створити черговий проміс.

Згідно стандарту, не «може» створити, а створює. І тому початкове питання — чому цей колбек проміса має відповідати інакше, ніж колбек початкового проміса?

чому цей колбек проміса має відповідати інакше, ніж колбек початкового проміса?

Іншими словами, ви питаєте: «Чому у then немає resove/reject?», — так жеш? Вище я розписав чому це не так.

Якщо вас заплутує, що і сам then повертає проміс, який, типу, так само можна ж вирішити, то думайте про цей проміс як про «транзитний проміс», який просто передає далі результат, він миттєво вирішується зразу після запуску колбеку, який йому передали (якщо сам колбек не створює свій проміс).

Ну а оскільки спочатку запускається колбек, і лише потім створюється «транзитний проміс», то зрозуміло що у рамках колбеку не можна його вирішити.

Вище я розписав чому це не так.

І я сказав, чому це не аргумент. Що робитимемо з цим далі? :)

він миттєво вирішується зразу після запуску колбеку, який йому передали.

Як він може «миттєво вирішуватись», коли він приймає за результат значення, що повертається з його колбеку?

Ну а оскільки спочатку запускається колбек, і лише потім створюється «транзитний проміс»

Тобто цей проміс не може бути «миттєво вирішений» ще до запуску колбеку. Чи в нього є машина часу? ;)

то зрозуміло що у рамках колбеку не можна його вирішити.

Якщо він існує в двох різних часах одночасно — так, не можна. Але якщо повернутись в наш всесвіт — є всі підстави вирішити.

Як він може «миттєво вирішуватись», коли він приймає за результат значення, що повертається з його колбеку?

Коли він «миттєво вирішується» після запуску колбеку, то йому вже відомий результат, який повертає колбек.

Тобто цей проміс не може бути «миттєво вирішений» ще до запуску колбеку. Чи в нього є машина часу? ;)

Де ви прочитали про вирішення промісу then до запуску колбеку?

Якщо він існує в двох різних часах одночасно — так, не можна.

Які мої коментарі вас наштовхнули на такі роздуми?

Де ви прочитали про вирішення промісу then до запуску колбеку?

У вас же:

>>

він миттєво вирішується зразу після запуску колбеку
Коли він «миттєво вирішується» після запуску колбеку, то йому вже відомий результат, який повертає колбек.

Тоді це називається не «після запуску», а «після повернення».

А так як проміс, створений цим then(), може бути повернений початковому агенту ще до виклика колбеку початкового промісу — проміс існує, і нема причини створювати його з іншими правилами, ніж початковий.

А так як проміс, створений цим then(), може бути повернений початковому агенту ще до виклика колбеку початкового промісу...
Таке не може бути за визначенням. Наведіть приклад такого коду.
Таке не може бути за визначенням.

Ваші визначення хибні.

Наведіть приклад такого коду.

var Promise = require('promise');
var r1 = 0;
var r2 = 0;

var p1 = new Promise(
  function(resolve, reject) {
    setTimeout(function() {
      resolve(1);
      }, 100);
  }
);
var p2 = p1.then(
  function(result) {
    console.log("result=" + result);
    r1 = result;
    return result + 2;
  }
)
var p3 = p2.then(
  function(result) {
    r2 = result;
    console.log("r2=" + result);
  }
)
console.log("all promises started");

setTimeout(function() {
  console.log("exiting");
  process.exit(0);
}, 200);

Результат виконання:


all promises started
result=1
r2=3
exiting

Тобто, p1, p2, p3 створені до повернення колбеку p1.

Спочатку наведу вашу цитату, твердження в якій я заперечую:

А так як проміс, створений цим then(), може бути повернений початковому агенту ще до виклика колбеку початкового промісу...

Тепер запустимо ваш код, але по-правильному, тобто додамо в лог подію, коли стартує виконання колбеку в першому промісі (див. console.log("one"); // це я додав):

var Promise = require(’promise’);
var r1 = 0;
var r2 = 0;

var p1 = new Promise(
function(resolve, reject) {

console.log("one"); // це я додав

setTimeout(function() {
resolve(1);
}, 100);
}
);
var p2 = p1.then(
function(result) {
console.log("result=" + result);
r1 = result;
return result + 2;
}
)
var p3 = p2.then(
function(result) {
r2 = result;
console.log("r2=" + result);
}
)
console.log("all promises started");

setTimeout(function() {
console.log("exiting");
process.exit(0);
}, 200);

Результат виконання:

one
all promises started
result=1
r2=3
exiting
Тепер запустимо ваш код, але по-правильному,

Нічого «правильного» тут нема. Це повноцінний race condition: one може зʼявитись до all promises started, а може — після. Жодної гарантії.

Але, навіть якщо якась конкретна реалізація завжди стартує перший колбек до повернення з конструктора початкового проміса — p2 і p3 створені гарантовано до повернення з 1го колбеку. Я казав саме про це.

Жодної гарантії.

Це ж можна задебажити. Вмієте запускати дебаг? VS Code вміє виконувати debug, він чітко показує, що після запуску new Promise, зразу починає виконуватись перший колбек.

Це ж можна задебажити.

Що «задебажити»? Гарантію? Вона або є в стандартах і закріплена у коді, або її нема.

Задебажити можна послідовність виконання команд. Якщо б дебаг показував, що спочатку створюються проміси, а лише потім виконується кожен із колбеків, то там вже гарантії б не було, — було б не зрозуміло на якому із циклів віртуальної машини JavaScript буде виконуватись той чи інший колбек... хоча мабуть і там би питань не було — колбеки будуть запускатись у порядку їх додавання до черги.

Ви пишете:

Жодної гарантії.

Але в документації Promise говориться:

Parameters

executor
A function that is passed the arguments resolve and reject. The executor function is executed immediately by the Promise implementation, passing resolve and reject functions (the executor is called before the Promise constructor even returns the created object). The resolve and reject functions, when called, resolve or reject the promise respectively. The executor normally initiates some asynchronous work and then, once that completes, calls either the resolve or reject function to resolve the promise or else reject it if an error occurred

1. Це не офіціальна документація, це MDN. A+ такої вимоги не має. ES7 має, але до нього ще дожити треба.
2. Ви знову ухиляєтесь від прямої відповіді. Справа не в часі повернення готового p1.

Если вы в конструктор промиса передает функцию которая немедленно возвращает значение — то зачем вообще промис? Вызывайте функцию непосредственно. Promise resolve/reject — просто для удобства.

Для цепного вызова нескольких (в т.ч. асинхронных) функций. И отлова ошибок в этом цепном вызове. (learn.javascript.ru/promise#цепочки-промисов)

Ні, для цього не обов’язково викликати саме new Promise, можна обмежитись і Promise.resolve().then()

Да, так и есть. Вопрос, как мне показалось, о другом.

А, ну да, справді. Треба вже мабуть мені йти спати =)

то зачем вообще промис?

Это вопрос к Третяку, не ко мне — это же он настаивает на принципиальности фишки, что коллбэк исходного промиса вызывается немедленно.
Я бы как раз предпочёл вариант, что коллбэк свободно может вызываться асинхронно, если удобно исполняющей среде — это просто логичнее.

Promise resolve/reject — просто для удобства.

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

Зараз почав ближче знайомитись із RxJS Observable, на їх фоні проміси — то «детский лепет» (в плані вивчення). Щоправда, може це таке враження склалось через те, що спробував їх вивчити за вечір...

Уважаемые нодисты, один вопрос, если позволите...

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

Анализ показал наличие большого кол-ва фатальных исключений, предположительно вызванных асинхронно, типа:

Error: write EPIPE
    at exports._errnoException (util.js:746:11)
    at WriteWrap.afterWrite (net.js:777:14)

И ряд других, описание ошибки неинформативно, в stack trace только несколько вызовов в кишках самой ноды.

Интересно, как уважаемое сообщество борется с подобными ошибками. Я понимаю, что где-то кто-то что-то накосячил, типа не тот аргумент передал или еще что-то, и это вызывает ошибку, но искать в ~100k строк асинхронного спагетти кода проекта (и еще неепическом кол-ве притянутых пакетов) где именно проблема — непродуктивно, а описание ошибки даже не пытается намекнуть на то, что и где сломано.

Я, в принципе, уже близок к тому, чтоб сказать клиенту, что система уже не просто так странно пахнет, и ее надо переписать на джаве, но, может, еще есть какая-то надежда?

Після вчорашнього дайджеста на ДОУ, у мене Node.js ще деякий час буде асоціюватись із цим фрагментом (див. текст у ньому =)

Що стосується питання, то в документації є пояснення помилки EPIPE:

EPIPE (Broken pipe): A write on a pipe, socket, or FIFO for which there is no process to read the data. Commonly encountered at the net and http layers, indicative that the remote side of the stream being written to has been closed.

І якщо код написаний без try/catch для роботи із сокетами, наприклад, то його треба справді викидати повністю на смітник.

Що стосується питання, то в документації є пояснення помилки EPIPE:
— Где мы?
— На воздушном шаре
І якщо код написаний без try/catch для роботи із сокетами, наприклад, то його треба справді викидати повністю на смітник.
Я так понимаю, там не просто try/catch, там еще нужно во куче мест расставлять .on("error"...)?

И, собственно, в нормальных языках, даже если пишешь без какой-то обрабокти ошибок, когда что-то падает, в т.ч. и связанное с сокетами, по stack trace можно определить виновника, здесь же описание ошибки ни дает абсолютно никакого намека на то, где и что искать.

— Где мы?— На воздушном шаре
В смислі, ви хочете щоб по наведеному фрагменті
Error: write EPIPE
    at exports._errnoException (util.js:746:11)
    at WriteWrap.afterWrite (net.js:777:14)
з описом “не понятно где ошибка”, вам дали діагноз більш розгорнутий, ніж я написав? — Удачі.

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

И зачем тогда вопросы задавать ? systemd — тяжело, домен — устарел, supervisord — отстой , монит пока борода не отрастет использовать не буду.. капризуля блин.

Так я вопросов и не задавал :)

Если используемая либа/код написаны более-менее ок, то возможно если запустить систему предварительно выставив пару переменных окружения типа «DEBUG=* NODE_ENV=development node ...» стектрейсы станут более информативными. Или можно попытать счастье в сервисе подобном snyk.io (возможно в системе притаились забагованные некролибы).
Если то, что выше не помогло, то возможно проблема именно в коде самой системы и "ее надо переписать на джаве"/хаскеле/эрланге.

Ну или если же времени много, можно помедитировать над v8 профайлером, возможно там будет больше инфы что откуда вызывается и к чему приводит.

"ее надо переписать на джаве"/хаскеле/эрланге.
Да, именно так и надо сделать.

PS: заглянул в код ноды, вполне вероятно, что ошибка возникает в libuv (github.com/libuv/libuv ), посему стектрейса и нет и он не появится, несмотря на грациозные танцы с бубном.
Возможно там крутится ее древняя версия с багами. Может под какие-то буферы памяти не хватает... Копайте в сторону дебага libuv или если терять нечего — можно попробовать её обновить.

Перепиши все на Go — и все проблемы как рукой снимет. Подробности недавно обсуждались на hacker news.

Якщо б ви колись писали на Node.js, а через років два (хоча б) перейшли на Go і теж пописали на ньому хоча б два роки, от тоді можна було слухати ваше хаяння Node.js.

Поки що усі ваші коментарі, які я бачу на ДОУ, схожі на «не знаю, не пробував, але Node.js — отстой».

Достаточно почитать топики наподобие этого, где рассказывается про проблемы node.js:
— callback hell
— сложности с пониманием базовых принципов promises
— непонимание, в каких местах нужно расставлять async / await
— неинформативные сообщения об ошибках, из которых не понятно, что, где и почему пошло не так
— сложности с корректной обработкой ошибок

После этого погуглить «switching from node.js to golang», чтобы больше никогда не возвращаться к ужасам node.js.

Джаваскрипт поламаний починаючи з чисел.
console.log(12345678901234567890)

В любом языке программирования все проблемы можно условно поделить на 2 большие группы: проблемы языка и проблемы дураков.
Так вот то, о чем вы говорите относится к последней категории. И заключается эта проблема в неумении или нежелании дураками чтения мануалов и доков.

В ЯП существуют разные подходы к борьбе с дураками, однако еще нет ни одного языка, который бы поборол их, ибо оные крайне изобретательны. Даже GO с его строгим компилятором бессилен перед дураками, которые в силу отсутствия в языке знакомых им конструкций типа дженериков и т.п. порождают кучу «копипаст хелла». В Java, наоборот, таких конструкций и паттернов с ними превеликое множество, чем дураки и пользуются, порождая мегоэнтерпрайз с фабриками фабрик для создания фабрик. Джаваскрипт, в свою очередь, крайне либерален. И если дурак захочет отстрелить себе ногу, js ему в этом очень поможет предоставив на выбор арсенал оружия. В функциональных языках дела обстоят получше, ибо большинство отсеивается на первом этапе со словами «о, боже, сколько тут скобочек», но особо упоротые таки проникают и вносят разброд и смуту.

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

Даже GO с его строгим компилятором бессилен перед дураками, которые в силу отсутствия в языке знакомых им конструкций типа дженериков и т.п. порождают кучу «копипаст хелла»
Копипасту, в отличие от неравильно обработанных ошибок, неправильно примененных калбэков или не к месту использованных паттернов проектирования, намного легче обнаружить и пофиксить. Также бывают случаи, когда немного копипасты делает код проще в понимании и в дальнейшем сопровождении.

Одна из основных целей Go — ограничить дураков в способах отстрелить себе ногу. IMHO, с этой целью он отлично справляется.

Я в душе мизантроп, поэтому мне больше нравится смотреть как дураки отстреливают себе конечности.

Відсутність цілих чисел як типу — це проблема мови, ви що! Використовувать за замовченням флоати всюду, які мають всім відомі обмеження — верх ідіотизму розробиників. Я звичайно розумію, що джс розробявся для дрібних скриптів типу помінять колір при кліку, але це каже не на його користь.

А еще из-за однопоточности nodejs приходится устраивать танцы с бубном, если нужно обновить огромный in-memory конфиг (обновление которого требует много CPU), при этом сохраняя приемлемое время отклика сервиса под большой нагрузкой. На go для этого достаточно одной строчки кода:

go updateHugeConfig()
callback hell
Я почав використовувати Node.js, коли вже «на повну» використовувались проміси, а зараз ще й можна (із TypeScript, Babel) писати async/await функції:
async testAsync()
{
  let data = await getDataFromDb().catch( myHandlerErrors );
  console.log(data);
}
сложности с пониманием базовых принципов promises

Хто б казав про це, — gopher? Я значно частіше зустрічаю в інтернеті відгуки за Go, що поріг входження в нього значно вищий, ніж це є у Node.js.

непонимание, в каких местах нужно расставлять async / await
Припускаю, що ви кажете про відловлення помилок, так? Таке нерозуміння проходить, мабуть, в перший же день тестування цієї фічі. Дуже просте правило: функцію, перед якою ставите await, беріть у try/catch або у зовнішній then/catch, або використовуйте catch самої функції (як я показав вище у цьому коменті)
— неинформативные сообщения об ошибках, из которых не понятно, что, где и почему пошло не так
— сложности с корректной обработкой ошибок
Усі джуни це проходять, причому не лише у Node.js, і якщо на цьому етапі ви не дійдете хоча б до мідла, то й будете писати подібні коментарі...
сложности с пониманием базовых принципов promises
Хто б казав про це, — gopher?

Вы же сами создали этот топик, в котором признаетесь в непонимании promises и просите помощи у читателей :)

Ну і? Я десь писав, що проміси це простіше простого?

Я десь писав, що проміси це простіше простого
зачем тогда менять шило на мыло — callback hell на promise hell? В Go нет ни того, ни другого. Поэтому я с уверенностью утверждаю, что Go проще, чем node.js. И еще на порядок быстрее.
зачем тогда менять шило на мыло — callback hell на promise hell? В Go нет ни того, ни другого.

Зато есть channel hell, blocking hell и panic hell.

Лише серед диванних теоретиків та неосиляторів :-)

Сказал апологет неосиляторов JS :)

Усі джуни це проходять, причому не лише у Node.js, і якщо на цьому етапі ви не дійдете хоча б до мідла, то й будете писати подібні коментарі...

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

Артем розказував не раз на ДОУ свою формулу «сеньйора» — позиціонуй себе як сеньйора, проси більше грошей і ти станеш ним =).

А якщо серйозно, то сеньйори можуть бути одночасно й джуніорами, бо прогалини в знаннях є у кожного...

Я значно частіше зустрічаю в інтернеті відгуки за Go, що поріг входження в нього значно вищий, ніж це є у Node.js.
Во-первых, не намного он и выше. Во-вторых, это хорошая защита от безнадежных дураков, из-за которых php, а теперь уже и nodejs, получил дурную славу.

Вы не читайте топики, а пишите код :)
Все, что вы описали — это отсутствие знания языка, который используется.
Или любой вася открывает редактор и моментально начинает кодить на Go?

Все, что вы описали — это отсутствие знания языка, который используется.
Я описал общепризнанные проблемы nodejs. Вы хотите сказать, что эти проблемы возникают из-за того, что бльшинство программистов nodejs не знают языка, на котором пишут программы?
callback hell
сложности с пониманием
непонимание
сложности
Такими словами описываются общепризнанные проблемы не языка, а именно программистов.

Да, большинство просто не знают языка. Не забывайте, что сейчас каждый третий (образно) — это «яваскрипт девелопер», из которых многие — это jquery-developer. Вот и лезут в ноду, потому что «там тот же JS». Но есть люди, которые сидят, кодят и, если чего-то не понимают, то читают доки/эксперементируют, а не пишут статьи, что нода говно, потому что там промисы сложные.
P.S.: Почему-то вижу параллель с фрилансом. Все пишут, что это «формошлепство», «работа за еду» и т.д., но множество людей сидит и зарабатывает бабло.
P.P.S.: лично я не осиливаю обилие скобок в Objective-C, но это же не повод писать статью на тему «как перейти с Obj-C на Go» :)

Такими словами описываются общепризнанные проблемы не языка, а именно программистов.
Зачем использовать язык программирования, который на первый взгляд очень прост, но его не могут корректно использовать большинство программистов? IT-мазохизм какой-то :)

З появою TypeScript розхлябаність у JavaScript дуже сильно зменшується. Я багато разів чув за статичну типізацію, коли програмував на РНР і не розумів для чого вона потрібна.

TypeScript для мене став абсолютно неочікуваним відкриттям, зокрема, завдяки статичній типізації. Парадоксальним для мене став факт — зі статичною типізацією програмувати стало значно-значно простіше. Ще на етапі написання коду, редактор може чітко вказати на помилку, або ж зробити підказку по наявності властивостей чи типів... Також із статичною типізацією ти фактично пишеш документацію, яку можуть розуміти як люди так і IDE, тобто те що раніше писалось в коментарях, — стало значно розвинутішим.

Насчет фриланса плюсую, для себя нашел на нем интересные задачи, а не галимые СПА клепать для энетпрайза

плюсую. стектрейсы в асинхронном коде — небывают информативными.

проблема в том что джс сильно зависит от сознательность программистов

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

Гляньте коментар (з багатьма плюсами) під цією відповіддю: stackoverflow.com/a/2932410/1716560

console.trace увы не панацея. Если ошибка произойдет, например где-то в недрах v8 или libuv, то никакой console.trace не поможет.

Саме тому я написав про “коментар із багатьма плюсами”:

console.log(err.stack) and console.trace() do not give you same results. Whereas err.stack gives you the stack trace for the err object itself (functioning the way we all normally think of exceptions), console.trace() will print out the call stack at the point where console.trace() is being called. So if you catch some error being thrown by some deeper layer of code, console.trace() will not contain that deeper layer code in the stack trace since that code is no longer on the stack. However, console.log(err.stack) will contain the deeper layers as long as it threw an Error object.

Именно его я и читал. Если ошибка возникает в плюсовом коде, то стека не будет, он вернет код ошибки (напр. EPIPE) и досвидос. И существенной разницы между console.log и console.trace не будет, потому что я и так знаю где у меня этот console.log вызывается. А большего console.trace не покажет.

И существенной разницы между console.log и console.trace не будет
Там говориться про різницю між err.stack та console.trace()

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

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

Что из них позволяет сразу выводить произвольный текст, который прицеплен к трейсу автором кода (предполагаем, что он «сознательный» и обеспечил это)?

а насчет решений, я вам могу развечто дать это линк www.google.pl/...=2#q=js async stack trace

можете глянуть эту либу andreasmadsen.github.io/trace

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

Ще одна трохи неочевидна річ з використанням Promise.reject(). Наступний код в консоль виведе ’this is next code’, незважаючи на те що начебто запущено Promise.reject() у попередньому then:

Promise.resolve()
.then( () => { Promise.reject() } )
.then( () =>
{
  console.log('this is next code');
})

А вже наступний код спрацює так, як очікувалось. Тобто виходить, що Promise.reject() або Promise.resolve() треба в будь-якому випадку повертати:

Promise.resolve()
.then( () => Promise.reject() )
.then( () =>
{
  console.log('this is next code');
})

Запуск Promise.reject() або Promise.resolve() без return не мають сенсу, на відміну від їх аналогів при запуску new Promise():

new Promise( (resolve, reject) =>
{
  reject();
} )
.then( () =>
{
  console.log('this is next code');
})

Я ж написав «трохи неочевидна». Неочевидно що Promise.reject() не запускає ту саму функцію, що і колбек reject() при запуску new Promise().

Просто не нужно забывать, что нужно что-то возвращать, а не надеяться на магию :)
Тем более не стоит сравнивать несравниваемые вещи.

«new Promise» вернет промис, в данном случае зареджектеный.
Просто вызов «Promise.reject()» внутри любой ветки (then/catch) ничего не даст, т.к. ветка вернет undefined — а это не реджект.
В общем случае вместо «Promise.reject()» может возвращаться другой промис и тогда уже не будет никакой аналогии, что, якобы, и там и там есть «reject».

а не надеяться на магию
вот это золотые слова!

почти все что люди считают неочевидным — надежда что есть какаято магия, а ее нет

Достаточно почитать promisesaplus.com чтоб все стало очевидно

Для мене стало це очевидним зразу після першої моєї помилки, коли я сплутав режекти. Коли ж я придивився до них, то ясно стало що Promise.reject() ніяким чином не може зарежектити той then, де він викликається, бо він не пов’язаний з ним.

За лінк дякую, гляну що там пишуть.

We have a problem with promises — вже більше року статті, але актуальна завжди.

... promises aren’t perfect. It’s true that they’re better than callbacks, but that’s a lot like saying that a punch in the gut is better than a kick in the teeth. Sure, one is preferable to the other, but if you had a choice, you’d probably avoid them both.

О, так я через цю статтю й створив дану тему, точніше через показаний третій приклад використання:

doSomething().then(doSomethingElse())
  .then(finalHandler);

Нижче дається відповідь (натисніть Ctrl + F шукайте слова «Puzzle #3»).

doSomething
|-----------------|
doSomethingElse(undefined)
|---------------------------------|
                  finalHandler(resultOfDoSomething)
                  |------------------|

Тобто, якщо у межах then повертається не проміс, а колбек з «відкритим кодом» (чи й просто «відкритий код»), то наслідки можуть бути зовсім неочікуваними.

Я зрозумів де проблема :-) Це не «приклади використання», а головоломки :-)
Так як написано в #3 писати не можна. Звичайно, якщо метою не є заплутати того, хто буде сапортити цей код.

Я ж так і не написав, у своєму прикладі я взяв код у рамки колбеку, але виникли додаткові запитання.

Тобто, досить чітко зрозуміло якою буде поведінка, коли в then повертається проміс. А вже коли частина коду йде «відкритою», а далі все ж є Promise.resolve(), то тут вже виникали питання...

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

промисы — моветон давно, однако
с RxJS (node-байнды) путь джедая отыщешь ты

зы: с картинками и шлюхами по ES6 промисам:
1. developer.mozilla.org/...ce/Global_Objects/Promise
2.1 www.2ality.com/...promises-foundations.html
2.2 www.2ality.com/.../10/es6-promises-api.html

промисы — моветон давно, однако
с RxJS (node-байнды) путь джедая отыщешь ты
как там у событий с chaining’ом(then(...).then(...)) и синхронизацией(Promise.all)?

там все хорошо
монадический bind для Observable (это как `then` для промиса) — это `flatMap` (и его инварианты `concatMap`, `switchMap`)

flatMap :: Observable a -> (a -> Observable b) -> Observable b
reactivex.io/...on/operators/flatmap.html
flatMap — это сердце rx.

Promise.all похож на forkJoin, однако существует более удобный оператор combineLatest.
combineLatest :: [Observable a] -> Observable b
rxmarbles.com/#combineLatest

спасибо, признаю, не в курсе этого
хорошо, что отписались

Внимательно читать от начала до конца: promisesaplus.com

Але чи доречно ці методи використовувати десь всередині колбеку, наприклад так:
А зачем, если по цепочке then можно возвращать как промисы, так и обычные значения?
В такому разі наступний then, буде очікувати поки зарезолвиться попередній then, чи ні?
Да, будет.
спочатку другий, а потім лише перший код.
Нет, такого не случится.
спочатку другий, а потім лише перший код.
Работает так, как и положено — цепочка по порядку.
А зачем, если по цепочке then можно возвращать как промисы, так и обычные значения?
Ну під “обычными значениями” ви мабуть маєте на увазі ті значення, що не повертають проміс. Класика жанру:
Promise.resolve()
.then( () =>
{
  return setTimeout( () =>
  {
    console.log('done first code');
  }, 2000);
} )
.then( () =>
{
  console.log('done second code');
})

В результаті виконається спочатку другий, і лише потім перший код.

Вам нужно разобраться получше в подноготной промисов, что это такое и в обще, что такое event loop. Тогда все станет ясно.

Ну почему же, сначала выполнятся первый :) Только результат выполнения первого then — это результат, который вернет setTimeout.
Поэтому, все верно:
— выполнился первый then (setTimeout)
— выполнился второй then (console.log(’done second code’);)
— выполнилась функция внутри setTimeout (console.log(’done first code’);)

Я з вами погоджуюсь, на перший погляд це очевидно — в межах then, спочатку виконуються всі «неколбеки», і лише потім колбеки. І оскільки в then не повертається проміс, то поточний then думає, що все виконалось, а тому передає естафету далі...

Коли я створював даний топік, то я до цього догадувався, але все ж вирішив перепитати у знаючих...

Подписаться на комментарии