For example I found some api library that is based on promises, and I need to issue api requests using this library in some interval, infinite times (like usual back-end loop). This api requests - actually chain of promises.
So, if I write function like:
function r(){
return api
.call(api.anotherCall)
.then(api.anotherCall)
.then(api.anotherCall)
...
.then(r)
}
Will it cause stack overflow?
Solutions that I come up with is to use setTimeout for a call of r recursively.
function r(){
return api
.call(api.anotherCall)
.then(api.anotherCall)
.then(api.anotherCall)
.then(()=>{setTimeout(r, 0)})
}
So setTimeout will call r actually only when call stack is empty.
Is it good solution, or there is some standard way of calling promises recursively?
Will this cause stackoverflow?
No, it will not. Per the promise specification, .then() waits for the stack to completely unwind and is then called after the stack is clear (essentially on the next tick of the event loop). So, .then() is already called asynchronously after the current event is done processing and the stack is unwound. You do not have to use setTimeout() to avoid stack build-up.
Your first code example will not have any stack build-up or stack overflow, no matter how many times you repeat it.
In the Promises/A+ specification, section 2.2.4 says this:
2.2.4 onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].
And, "platform code" is defined here in 3.1:
“platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such as setTimeout or setImmediate, or with a “micro-task” mechanism such as MutationObserver or process.nextTick. Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or “trampoline” in which the handlers are called.
The ES6 promise specification uses different words, but generates the same effect. In ES6, promise .then() is performed by enqueing a job and then letting that job get processed and the job only gets processed when no other code is running and the stack is empty.
This is how running such as job is described in the ES6 spec:
A Job is an abstract operation that initiates an ECMAScript computation when no other ECMAScript computation is currently in progress. A Job abstract operation may be defined to accept an arbitrary set of job parameters.
Execution of a Job can be initiated only when there is no running execution context and the execution context stack is empty. A PendingJob is a request for the future execution of a Job. A PendingJob is an internal Record whose fields are specified in Table 25. Once execution of a Job is initiated, the Job always executes to completion. No other Job may be initiated until the currently running Job completes. However, the currently running Job or external events may cause the enqueuing of additional PendingJobs that may be initiated sometime after completion of the currently running Job.
Related
In Javascript, the callback (of the awaited async function) runs only when the call stack is empty. If there are multiple callbacks, then they are queued (in the event loop queue) such that only 1 callback runs in its entirety before the next callback gets change to run.
In case of C# (assuming ConfigureAwait is not set to false - that is - the callabck will run in the same thread; also assuming there is syncronization context [winforms]):
Does the callback run immediately without waiting for call stack to be empty?
Suppose there are multiple callbacks queued, then while 1 callback is running, and another callback is ready for execution, then does the running callback suspend while the new callback wants to run? Or is it sequential?
The answer is that it depends on the current SynchronizationContext and, if that is null, the current TaskScheduler.
When the awaited Task completes, and you have configured the Awaitable to capture the context and recover on it, then almost certainly that continuation cannot continue immediately (the thread is likely busy executing something else). All that can be done is to schedule the continuation for later execution on the current SynchronizationContext, which is done by calling SynchronizationContext.Post().
How this Post method is implemented depends on the SynchronizationContext (which is abstract). If you are using WPF, the SynchronizationContext is a DispatcherSynchronizationContext. There, Post is implemented by a call to Dispatcher.BeginInvoke() with DispatcherPriority.Normal.
In other words, your continuation gets queued on the dispatcher for later execution with normal priority. Other delegates may have higher priority (e.g. DispatcherPriority.Input), but eventually your delegate gets executed on the dispatcher.
Two things to notice:
BeginInvoke simply queues your delegate. It does not execute it right away. In other words, queuing your delegate is really fast.
The above explanation should illustrate why async-await is just syntactic sugar. You could achieve all these calls yourself, but the compiler does it for you, making your code flow more naturally.
For example I found some api library that is based on promises, and I need to issue api requests using this library in some interval, infinite times (like usual back-end loop). This api requests - actually chain of promises.
So, if I write function like:
function r(){
return api
.call(api.anotherCall)
.then(api.anotherCall)
.then(api.anotherCall)
...
.then(r)
}
Will it cause stack overflow?
Solutions that I come up with is to use setTimeout for a call of r recursively.
function r(){
return api
.call(api.anotherCall)
.then(api.anotherCall)
.then(api.anotherCall)
.then(()=>{setTimeout(r, 0)})
}
So setTimeout will call r actually only when call stack is empty.
Is it good solution, or there is some standard way of calling promises recursively?
Will this cause stackoverflow?
No, it will not. Per the promise specification, .then() waits for the stack to completely unwind and is then called after the stack is clear (essentially on the next tick of the event loop). So, .then() is already called asynchronously after the current event is done processing and the stack is unwound. You do not have to use setTimeout() to avoid stack build-up.
Your first code example will not have any stack build-up or stack overflow, no matter how many times you repeat it.
In the Promises/A+ specification, section 2.2.4 says this:
2.2.4 onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].
And, "platform code" is defined here in 3.1:
“platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such as setTimeout or setImmediate, or with a “micro-task” mechanism such as MutationObserver or process.nextTick. Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or “trampoline” in which the handlers are called.
The ES6 promise specification uses different words, but generates the same effect. In ES6, promise .then() is performed by enqueing a job and then letting that job get processed and the job only gets processed when no other code is running and the stack is empty.
This is how running such as job is described in the ES6 spec:
A Job is an abstract operation that initiates an ECMAScript computation when no other ECMAScript computation is currently in progress. A Job abstract operation may be defined to accept an arbitrary set of job parameters.
Execution of a Job can be initiated only when there is no running execution context and the execution context stack is empty. A PendingJob is a request for the future execution of a Job. A PendingJob is an internal Record whose fields are specified in Table 25. Once execution of a Job is initiated, the Job always executes to completion. No other Job may be initiated until the currently running Job completes. However, the currently running Job or external events may cause the enqueuing of additional PendingJobs that may be initiated sometime after completion of the currently running Job.
I don't understand why resoved Promise delay .then() argument call?
example:
var myPromise = Promise.resolve();
console.log(myPromise);
myPromise.then(()=>console.log('a'));
console.log('b');
console return:
> Promise { <state>: "fulfilled", <value>: undefined }
> "b"
> "a"
If myPromise is fulfilled, why .then() don't call imediatly resolve function?
Because, by specification, promises call their resolve handler AFTER the current thread of execution unwinds and finishes back to "platform code". That guarantees that they are always called asynchronously.
So, thus you see the console.log('b') first as that thread of execution finishes and then the resolve handler is called where you see the console.log('a').
From the Promises/A+ specification:
2.2.4 onFulfilled or onRejected must not be called until the execution
context stack contains only platform code. [3.1].
And, here's note [3.1]:
Here “platform code” means engine, environment, and promise
implementation code. In practice, this requirement ensures that
onFulfilled and onRejected execute asynchronously, after the event
loop turn in which then is called, and with a fresh stack. This can be
implemented with either a “macro-task” mechanism such as setTimeout or
setImmediate, or with a “micro-task” mechanism such as
MutationObserver or process.nextTick. Since the promise implementation
is considered platform code, it may itself contain a task-scheduling
queue or “trampoline” in which the handlers are called.
This is done to provide consistent execution order so no matter when the promise is resolved (synchronously or asynchronously), the then() handlers are always called in the same timing relative to other code. Since many promises are resolved asynchronously, the only way to make a given promise consistent no matter how it is resolved is to make them always call their .then() handlers asynchronously.
jfriend00's answer is correct. Allow me to elaborate on why. Let's say I got myPromise from somewhere. I don't know it's just a Promise.resolve, it might resolve asynchronously, and it might not.
myPromise.then(function(){
console.log("a");
});
console.log("b");
If the asynchronousity guarantee didn't exist - that would sometimes log a b and sometimes b a. This is a race condition, and is a terrible thing to have. Promises are not susceptible to this problem by design - and execution order is always guaranteed in new promises implementations and native promises in particular.
The actual implementation of running new jobs is via Job Queues. then enqueues a job to the job queue in the handler, and job queues are run after code is ready - this is specified here.
Many of my friends, who are using deeply some deferred/promises objects in their libraries, are often telling me, that to use timers in own implementation of it is an evil.
That it doesn't correspond to A+: https://github.com/promises-aplus/promises-spec
And that many libraries as jQuery and others don't use timers. So I've tried to find any timers in jQuery sources, which may relate to promises implementation, but no success:
https://github.com/jquery/jquery/blob/master/src/deferred.js
All, right, but I've found some notes in A+ description, which have confused me about using timers in it:
At Notes article:
Here "platform code" means engine, environment, and promise
implementation code. In practice, this requirement ensures that
onFulfilled and onRejected execute asynchronously, after the event
loop turn in which then is called, and with a fresh stack. This can be
implemented with either a "macro-task" mechanism such as setTimeout or
setImmediate, or with a "micro-task" mechanism such as
MutationObserver or process.nextTick. Since the promise implementation
is considered platform code, it may itself contain a task-scheduling
queue or "trampoline" in which the handlers are called.
So, I understood A+ didn't have strict rules about timer using or did it?
Help me, I'm rather confused.
You are confusing the use of setTimeout with "setting a timer" - Promises/A+ implementations typically use setTimeout to guarantee asynchronous execution of handler functions, not to delay execution by some time period.
Promises/A+ guarantees that the fulfilled and rejected methods are called asynchronously, regardless of when the promise is fulfilled. One way to guarantee async execution in a browser JS environment is to wrap a function call in setTimeout with a timeout of zero (the default).
jQuery does not guarantee asyc execution of fulfilled/rejected callbacks (which is a major design flaw), so an async wrapper call is not required.
Using timers in general in your applications is a bad practice (other than scheduling tasks, that is).
You can never be sure how long an action will take. So you end up doing one of three things:
You either allocate not enough time, in which case, stuff breaks because some things you expected to happen hadn't happened yet.
Or you allocate too much time, in which case you application is slow for no good reason
Or, worst case, you allocate just enough time, which causes your application to sometimes break, and sometimes work as expected.
I'm not sure about specs and stuff. But using timers and delays in your application in general is a bad idea.
Update
this an update to the question below and should help finding an answer
Taking up the answer from torazaburo who also quoted part of the prominent Javascript Promise/A+ definition I want to update the question here.
The Promise/A+ specification suggest in point 2.2.4 this:
onFulfilled or onRejected must not be called until the execution
context stack contains only platform code. 3.1.
and further explains
Here “platform code” means engine, environment, and promise
implementation code. In practice, this requirement ensures that
onFulfilled and onRejected execute asynchronously, after the event
loop turn in which then is called, and with a fresh stack. This can be
implemented with either a “macro-task” mechanism such as setTimeout or
setImmediate, or with a “micro-task” mechanism such as
MutationObserver or process.nextTick. Since the promise implementation
is considered platform code, it may itself contain a task-scheduling
queue or “trampoline” in which the handlers are called.
The very issue I look forward to find with this question is having as the crucial point that Promise implementation Javascript code is itself considered platform code and allows to not yield to the eventloop inbetween resolving subsequent promise via calling the onFulfilled onRejected functions associated. This is good in Node.js(server) as it avoids unnessary relinguishing back to the event-loop (leaving the execution stack), but also causes the challange in a Browser that since the execution stack is not exited in between resolving a potentially large number of Promises (which themselves can generate new Promises). Not leaving the execution stack and yielding to the event loop is causing in a Browser the undesired (blocking script warning/problem).
The "trampoline" task-scheduling of the Promise implementation which causes this needs however not necessary refrain from handing back the execution to the Javascript event loop from time to time. Such a feature would allow for using Promises for heavier tasks. Such an implementation for Promises for "long-running-code" is searched/asked for in this question.
Clarification: The "excessive lenght" is not the individual length of the onFulfilled function, but the joining together several those functions/callbacks as result of the Promise resolving process (when done in such a "trampoline" way). I am already aware that if one individual onFulfilled funciton is too long, this cannot be helped in any way by using any sort of Promise implementation.
The deal here is that the subsequent resolvement of x promises (within one excecution stack and hence without handing back to the Javascript event loop) can provoke an excessive length duration of Javascript code execution. This, when in a Browser is bad (because of blocking).
The question
In Javascript, Promises allow to deal with asynchronous programming tasks. Great!
There are already some implementations and libraries arround Q, WinJS or when.js to name just a few.
Having looked at then I see that they tackle some of the "special things" in Javascript asynchronous programming challanges.
Normally I perceive them to do this for promise resolution
Go to the internal list of promises
Check if the promise is fullfilled + run all the associated (via then(onFullfilled,onReject)) functions.
(in some cases we are done here)
(in other cases there will be still "pending" promises)
This case (4) is because to have them (the remaining promises) fullfilled would need the current Javascript Code (which is this very code for promise resolution) to stop running and allow JS event loop to happend (i.e asynchronous things like XHR-requests, or User-UI-interaction). To make this (4) work, the promise resolution normaly schedules a recall (i.e. via setTimeout/setImmediate) and continues after the event loop ran and hence maybe some of the "pending" promises have been settled (=rejected/fullfilled).
My worry is that the step 1 and 2 could be runnning for quite a some time, only releasing execution to the event loop in case it seems indicated to settle some of the "pending" promises. While "okay" in some cases (i.e. on the server/Node.js) it is quite problematic in a browers, because even though it was no problem to release execution to the event loop and have the UI not-blocking, this is not done in the implementations of promises I have seen.
My question therefore is:
Do you know a promise implementation (Javascript Promises library) that cares for the aspect:
to make "long-running-code-non-blocking-UI" in browser?
which would mean that the promise resolution would voluntarily release execution back to the event loop so that CSS animations, user input, mouse interaction, does get enough attention and that there will be no "Warning: Unresponsive script" message.
Any compliant promises implementation will not run the then functions synchronously, but rather only at the next tick. Therefore, your worry that "step 1 and 2 could be runnning for quite a some time" is unfounded.
From the Promises/A+ spec:
onFulfilled or onRejected must not be called until the execution context stack contains only platform code.
Here "platform code" means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack.
In other words, your formulation under 2) is incorrect. The promises implementation does not "run the associated functions", it schedules them.
This cannot help you--indeed there is no way to help you--if a handler itself is "long-running" code.
I think the solution could be to parse that long-running JavaScript code for example with https://github.com/NeilFraser/JS-Interpreter.
It will make the code be even slower, but you could specify the priority:
const myInterpreter = new Interpreter(myCode);
function nextStep() {
if (myInterpreter.step()) {
window.setTimeout(nextStep, 100/speed);
}
}
nextStep();