Я ищу, включает ли Promise.resolve(1);
какую-либо очередь mcirotask или нет? К нему не прикреплен обработчик .then() или .catch(). Только Promise.resolve(1); Пример:
const v = Promise.resolve(1);
Согласно спецификациям ECMA 2024, я прочитал следующие алгоритмы:
27.2.4.7 Promise.resolve (x)
Эта функция возвращает либо новое обещание, разрешенное с помощью переданного аргумент или сам аргумент, если аргумент представляет собой обещание, созданное этим конструктором.
1. Let C be the this value. 2. If C is not an Object, throw a TypeError exception. 3. Return ? PromiseResolve(C, x).
Примечание. Эта функция ожидает, что это значение будет функцией-конструктором. который поддерживает соглашения о параметрах конструктора Promise.
27.2.4.7.1 PromiseResolve ( C, x )
Абстрактная операция PromiseResolve принимает аргументы C ( конструктор) и x (значение языка ECMAScript) и возвращает либо нормальное завершение, содержащее значение языка ECMAScript или бросок завершение. Он возвращает новое обещание, разрешенное с помощью x. Он выполняет следующие шаги при вызове:
1. If IsPromise(x) is true, then a. Let xConstructor be ? Get(x, "constructor"). b. If SameValue(xConstructor, C) is true, return x. 2. Let promiseCapability be ? NewPromiseCapability(C). 3. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »). 4. Return promiseCapability.[[Promise]].
27.2.1.3.2 Функции разрешения обещаний
Функция разрешения обещаний — это анонимная встроенная функция, которая имеет Внутренние слоты [[Promise]] и [[AlreadyResolved]].
Когда функция разрешения обещания вызывается с разрешением аргумента, предпринимаются следующие шаги:
1. Let F be the active function object. 2. Assert: F has a [[Promise]] internal slot whose value is an Object. 3. Let promise be F.[[Promise]]. 4. Let alreadyResolved be F.[[AlreadyResolved]]. 5. If alreadyResolved.[[Value]] is true, return undefined. 6. Set alreadyResolved.[[Value]] to true. 7. If SameValue(resolution, promise) is true, then a. Let selfResolutionError be a newly created TypeError object. b. Perform RejectPromise(promise, selfResolutionError). c. Return undefined. 8. If resolution is not an Object, then a. Perform FulfillPromise(promise, resolution). b. Return undefined. 9. Let then be Completion(Get(resolution, "then")). 10. If then is an abrupt completion, then a. Perform RejectPromise(promise, then.[[Value]]). b. Return undefined. 11. Let thenAction be then.[[Value]]. 12. If IsCallable(thenAction) is false, then a. Perform FulfillPromise(promise, resolution). b. Return undefined. 13. Let thenJobCallback be HostMakeJobCallback(thenAction). 14. Let job be NewPromiseResolveThenableJob(promise, resolution, thenJobCallback). 15. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). 16. Return undefined. The "length" property of a promise resolve function is 1𝔽.
см. шаг 8.а
27.2.1.4 FulfillPromise (обещание, значение)
Абстрактная операция FulfillPromise принимает аргументы Promise ( Promise) и value (значение языка ECMAScript) и возвращает неиспользованное значение. При вызове он выполняет следующие шаги:
1. Assert: The value of promise.[[PromiseState]] is pending. 2. Let reactions be promise.[[PromiseFulfillReactions]]. 3. Set promise.[[PromiseResult]] to value. 4. Set promise.[[PromiseFulfillReactions]] to undefined. 5. Set promise.[[PromiseRejectReactions]] to undefined. 6. Set promise.[[PromiseState]] to fulfilled. 7. Perform TriggerPromiseReactions(reactions, value). 8. Return unused.
Прав ли я, говоря, что Promise.resolve(1) не включает в себя никаких микрозадач?
Если в приведенном выше примере вы добавите setTimeout(() => console.info('everyone'));
в начало, вы увидите, как теперь написано hello
there
everyone
, это потому, что вместо этого тайм-аут будет использовать очередь макрозадач, и она имеет более низкий приоритет, чем микрозадача, и обрабатывается во время следующего события. петля.
IOW: Promise.resolve(1);
действительно не включает никаких микрозадач, он становится актуальным везде, где у вас есть then
обратные вызовы, и именно при обработке очереди микрозадач. Обратите внимание, что, возможно, стоит отметить, что обработка очереди микрозадач на самом деле sync
, а не async
, даже если кажется, что это async
..
@Кейт, кажется, это необычное определение слова «синхронизация», которое ты там используешь.
@trincot Может быть, какое слово вы бы предложили вместо этого? По сути, я просто указываю на то, что очередь не обрабатывается асинхронно, IOW: вы даже можете заблокировать свой браузер, если вы связали обещания в цепочку без каких-либо реальных async
операций между ними.
Насколько я понимаю, в мире JavaScript термин асинхронный применяется всякий раз, когда функция выполняется/возобновляется движком при обработке очереди заданий, независимо от того, какая очередь заданий. Если ваша точка зрения заключается в том, что приоритет очереди микрозадач выше, чем у событий пользовательского интерфейса, то я бы сказал именно так.
@aLearner, похоже, процитировав спецификации, вы ответили на свой вопрос.
Для меня @trincot async
— это основной event
цикл, микрозадачи выполняются перед следующим циклом событий. Если в спецификациях указано, что обработка очереди microtask
классифицируется как async
, то пусть будет так. Но лично я не думаю, что это имеет какой-либо смысл.
Хотя для меня это имеет большой смысл.
@trincot const delay = () => {const t = Date.now()+1000; while (Date.now() < t);}; for (let l = 0; l < 10; l ++) { Promise.resolve().then(r => delay()) }
Это заблокирует браузер на 10 секунд, если обработка очереди микрозадач была async
это должно было заблокировать его только на 1 секунду 10 раз. например. использование setTimeout
с l*1000 действительно сделает это. Так что для меня это различие — нечто большее, чем просто приоритет. И независимо от терминологии именно это я и пытался донести.
Конечно, я понимаю вашу точку зрения. Это звучит так запутанно, что у вас другое понятие асинхронности.
@trincot Для меня, если что-то есть async
, то это то, что позволяет продолжать цикл событий основного потока, это было в случае, когда я писал код на C и мне приходилось возиться с PeekMessage
/ Postmessage
и т. д. В Javascript есть такие вещи, как fetch
/ setTimeout
и т. д., которые, по моему мнению, являются async
, и не блокируют основной цикл событий. И именно поэтому я хотел подчеркнуть, что setTimeout(fn)
не будет работать так же, как Promise.resolve().then()
. Лучший пример -> shorturl.at/hnj6R
Как я уже написал, я понимаю вашу точку зрения. Вы просто используете другую концепцию «асинхронности» и связываете ее с блокировкой пользовательского интерфейса. Для меня это другое понятие. На ваш взгляд, обратный вызов then
может выполняться синхронно. Но в сообществе JavaScript обычно используется слово «асинхронный», обратный вызов then
всегда выполняется асинхронно. Посмотрите, например, как слово асинхронный используется в пункте 3.1 спецификации Promises/A+.
Я думаю, что Кит хочет здесь отметить, что .then() является асинхронным, но ВНУТРИ очереди микрозадач САМ синхронизируется. Я ошибаюсь?
@trincot В этой версии 3.1 вы заметили -> Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or “trampoline” in which the handlers are called.
Техника setTimeout
уже давно не используется в браузерах. В некоторых отношениях if — это просто -> const q = [() => console.info('one'),() => console.info('two')]; for (const fn of q) fn();
, который запускается перед следующим тиком. Единственное отличие состоит в том, что очередь очищается браузером. Таким образом, вы бы не классифицировали этот фрагмент кода как async
?
@trincot, возможно, проблема здесь в том, что вы считаете разумным кодом async
и что современные браузеры делают с обещаниями. Насколько я понимаю, код async
вообще не использовался в реализации Promise в современных браузерах. В большинстве случаев различие не имеет значения, в конце концов, зачем создавать обещание ни для одного async
кода. Но, как я уже говорил, если у вас есть условные обещания, что когда-нибудь вы будете выполнять настоящий async
, а иногда и чистый sync
код, существует вероятность блокировки браузера или цикла событий узла. Со мной такое уже случалось, и это причина предупреждения.
@Keith Определение «асинхронного» заключается в том, что код выполняется вне обычного потока программы после того, как вызванная функция уже вернулась. Это касается как микрозадач, так и макрозадач.
Я хочу сказать, что там говорится, что вызовы then
асинхронны. Всегда. В нем упоминается микрозадача как реализация, но это не меняет концепции асинхронности. Ключевые слова — «свежий стек». Я понимаю, что вы говорите, но вы постоянно используете слово «асинхронный» не так, как я считаю стандартным (например, в реализации Promises/A+).
@trincot Тогда я возвращаюсь к своему первоначальному вопросу: какую терминологию вы бы использовали для обозначения блокирующего характера опустошения очереди микрозадач? Для меня sync
/async
кажется таким логичным, что слив очереди событий использует выполнение synchronous
. На самом деле я мог бы только что ответить на свой вопрос... Что я должен был сказать: blocking
, это помогает.?
Да, вы имеете в виду «блокировку» событий пользовательского интерфейса. Это другая концепция. Но помните, что даже если вы отправляете обратный вызов в очередь с более низким приоритетом, как в случае с setTimeout
, вы также блокируете пользовательские события во время выполнения кода JavaScript. Разница в том, что события с более низким приоритетом могут быть обслужены до того, как этот код начнет выполняться. Но как только это произойдет, оно также блокируется.
@Bergi Asynchronous computing is a paradigm where a user doesn't expect a workload to be executed immediately. Instead, the workload is scheduled for execution in the near future without blocking the application's latency-critical path
Для меня критический путь приложения, который я бы предположил, - это цикл событий. И оно блокирует его. В любом случае, я думаю, что высказал свою точку зрения, и если терминология в JS соответствует сказанному, то это достаточно справедливо.
Нет, Promise.resolve(1)
не предполагает никаких микрозадач. Как вы правильно заключили, он просто возвращает выполненное обещание: обещание создается, а затем разрешается (в частности: немедленно выполняется) без каких-либо реакций на расписание, прежде чем Promise.resolve
вернется.
27.2.1.4 FulfillPromise ( promise, value )
содержит 7. Perform TriggerPromiseReactions(reactions, value).
и в 27.2.1.8 TriggerPromiseReactions ( reactions, argument )
шагах находится 1. For each element reaction of reactions, do a. Let job be NewPromiseReactionJob(reaction, argument). b. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
. reactions
здесь будет неопределенным, как следует из 27.2.1.4 FulfillPromise ( promise, value )
. ТАК каким будет поведение 1. For each element reaction of reactions, do
в TriggerPromiseReactions(reactions, value)
?
Нет, это не «неопределенно». Он инициализируется пустым списком в конструкторе, поэтому цикл по этому списку просто ничего не делает.
Но почему оно попадет в 27.2.3.1 Promise ( executor )
? Я использую Promise.resolve(1)
, как указано в моей первоначальной задаче, поэтому он должен перейти к 27.2.4.7 Promise.resolve ( x )
, в соответствии с которым выполняется следующий оператор 4. Set promise.[[PromiseFulfillReactions]] to undefined
?
Он попадает в конструктор обещаний во время NewPromiseCapability(C)
. Обратите внимание, что promise.[[PromiseFulfillReactions]] = undefined
происходит только после «Let reactions
be promise.[[PromiseFulfillReactions]]
», который представляет собой пустой список, передаваемый TriggerPromiseReactions
.
Попробуйте ->
Promise.resolve('there').then(r => console.info(r)); console.info('hello')
Обратите внимание, как здесь написаноhello
there
. Само создание промиса не ставится в очередь, но обратные вызовыthen
выполняются во время опустошения очереди микрозадач перед следующим циклом событий.