I'm just trying to improve my understanding on how JavaScript Promises work. I've created the following situation:
LOG 'FOO'
RUN CALLBACK LOGGING 'CALLBACK'
LOG 'BAR'
Expect all functions to complete immediately (by this I mean they will not take an excessive/unknown amount of time to complete that you would use an async operation to complete) so that the above order of operations will happen in that order.
You can write this in the following way:
function foo(cb) {
// LOG 'FOO'
console.log('foo');
// RUN CALLBACK
cb();
}
function callback() {
// LOG 'CALLBACK'
console.log('callback');
}
foo(callback);
console.log('bar');
This produces the expected output according to the situation I specified at the beginning.
> foo
> callback
> bar
You could also write it in the following way:
function foo() {
return new Promise((resolve) => {
// LOG 'FOO'
console.log('foo');
return resolve(null);
});
}
function callback() {
// LOG 'CALLBACK'
console.log('callback');
}
foo().then(callback);
// LOG 'BAR'
console.log('bar');
This situation produces the following result:
> foo
> bar
> callback
This is where I am unclear as I am expecting foo to have completed immediately so that callback will run and log 'callback' before bar logs 'bar'
The relevant specs are here:
Promises/A+ point 2.2.4:
onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].
And note 3.1 (emphasis mine):
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.
ECMAScript 6.0 (based on Promises/A+) is a little harder to excerpt cleanly, but then resolves as in section 25.4.5.3.1:
Else if the value of promise's [[PromiseState]] internal slot is "fulfilled",
a. Let value be the value of promise's [[PromiseResult]] internal slot.
b. Perform EnqueueJob("PromiseJobs", PromiseReactionJob, «fulfillReaction, value»).
Else if the value of promise's [[PromiseState]] internal slot is "rejected",
a. Let reason be the value of promise's [[PromiseResult]] internal slot.
b. Perform EnqueueJob("PromiseJobs", PromiseReactionJob, «rejectReaction, reason»).
And the important EnqueueJob operation is defined in section 8.4 ("Jobs and Job Queues"), featuring this in its preface (bold is mine):
Execution of a Job can be initiated only when there is no running execution context and the execution context stack is empty. [...] 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.
In practice, this lets you make a few simple and consistent statements:
You can count on then or catch (etc) to always behave asynchronously, never synchronously.
You'll never see multiple then or catch handlers on the same stack, even if one Promise is explicitly resolved within another Promise. This also means that recursive Promise execution doesn't risk stack overflows as a normal function call might, though you can still run out of heap space if you're careless with recursive closures in a pathological case.
Time-consuming operations queued in a then or catch handler will never block the current thread, even if the Promise is already settled, so you can queue up a number of asynchronous operations without worrying about the order or promise state.
There will never be an enclosing try block outside of a then or catch, even when calling then on an already-settled Promise, so there's no ambiguity about whether the platform should handle a thrown exception.
This is not possible due to the way promises works.
Even promises which immediate resolves runs for the next tick, what you want is sync functions not promises.
See this for example:
setTimeout(function() {
console.log("test");
}, 0);
console.log("test2");
It is impossible to print test before test2 without removing the setTimeout function, because even if the wait parameter is 0, it will run for the next tick which means that it will run when all sync code has run.
I really don't mean to be blunt, but it is because that is the way the spec says that they work. If you have a need for a piece of code to run at a certain point after a code within a promise finishes, then you should utilize the promise chain. Once you introduce asynchronous code into the mix, it is a bad idea to try and mix it with dependent, synchronous, code.
Daisy chain promises whenever you need things to depend on asynchronous code:
function foo() {
console.log('foo');
return Promise.resolve();
}
function callback() {
console.log('callback');
}
function consoler() {
console.log('bar');
}
foo().then(callback).then(consoler);
Produces:
foo
callback
bar
Related
While debugging a set-up to control promise concurrency for some CPU intensive asynchronous tasks, I came across the following behavior that I can not understand.
My route to control the flow was to first create an array of Promises, and after that control the execution of these Promises by explicitly awaiting them. I produced the following snippet that shows the unexpected behavior:
import fs from 'node:fs';
import { setTimeout } from 'node:timers/promises';
async function innerAsync(n: number) {
return fs.promises.readdir('.').then(value => {
console.log(`Executed ${n}:`, value);
return value;
});
}
async function controllerAsync() {
const deferredPromises = new Array<Promise<string[]>>();
for (const n of [1, 2, 3]) {
const defer = innerAsync(n);
deferredPromises.push(defer);
}
const result = deferredPromises[0];
return result;
}
const result = await controllerAsync();
console.log('Resolved:', result);
await setTimeout(1000);
This snippet produces the following result (assuming 2 files are in .):
Executed 1: [ 'test.txt', 'test2.txt' ]
Resolved: [ 'test.txt', 'test2.txt' ]
Executed 2: [ 'test.txt', 'test2.txt' ]
Executed 3: [ 'test.txt', 'test2.txt' ]
Observed
The promises defined at line 15 are all being executed, regardless if they are returned/awaited (2 and 3).
Question
What causes the other, seemingly unused, promises to be executed? How can I make sure in this snippet only the first Promise is executed, where the others stay pending?
Node v19.2
Typescript v4.9.4
StackBlitz
What causes the other, seemingly unused, promises to be executed?
A promise is a tool used to watch something and provide an API to react to the something being complete.
Your innerAsync function calls fs.promises.readdir. Calling fs.promises.readdir makes something happen (reading a directory).
The then callback is called as a reaction when reading that directory is complete.
(It isn't clear why you marked innerAsync as async and then didn't use await inside it.)
If you don't use then or await, then that doesn't change the fact you have called fs.promises.readdir!
How can I make sure in this snippet only the first Promise is executed, where the others stay pending?
Focus on the function which does the thing, not the code which handles it being complete.
Or an analogy:
If you tell Alice to go to the shops and get milk, but don't tell Bob to put the milk in the fridge when Alice gets back, then you shouldn't be surprised that Alice has gone to the shops and come back with milk.
Promises aren't "executed" at all. A promise is a way to observe an asynchronous process that is already in progress. By the time you have a promise, the process it's reporting the result of is already underway.¹ It's the call to fs.readdir that starts the process, not calling then on a promise. (And even if it were, you are calling then on the promise from fs.readdir right away, in your innerAsync function.)
If you want to wait to start the operations, wait to call the methods starting the operations and giving you the promises. I'd show an edited version of your code, but it's not clear to me what it's supposed to do, particularly since controllerAsync only looks at the first element of the array and doesn't wait for anything to complete before returning.
A couple of other notes:
It's almost never useful to combine async/await with explicit calls to .then as in innerAsync. That function would be more clearly written as:
async function innerAsync(n: number) {
const value = await fs.promises.readdir(".");
console.log(`Executed ${n}:`, value);
return value;
}
From an order-of-operations perspective, that does exactly the same thing as the version with the explicit then.
There's no purpose served by declaring controllerAsync as an async function, since it never uses await. The only reason for making a function async is so you can use await within it.
¹ This is true in the normal case (the vast majority of cases), including the case of the promises you get from Node.js' fs/promises methods. There are (or were), unfortunately, a couple of libraries that had functions that returned promises but didn't start the actual work until the promise's then method was called. But those were niche outliers and arguably violate the semantics of promises.
Why does a function called after my promise execute before the promise's callback?
I read this in MDN, but didn't understand it
"Callbacks will never be called before the completion of the current
run of the JavaScript event loop."
I thought it meant that if I have any other statements after resolve() or reject() they will get executed before the callback is invoked. Though, that seems to be an incomplete understanding.
function myFunction() {
return new Promise( function(resolve, reject) {
const err = false;
if(err) {
reject("Something went wrong!!!");
}
else {
resolve("All good");
}
});
}
myFunction().then(doSuccess).catch(doError);
doOther();
function doError(err) {
console.log(err);
}
function doSuccess() {
console.log('Success');
}
function doOther() {
console.log("My Other Function");
}
Output:
My Other Function
Success
By specification, a promise .then() or .catch() callback is never called synchronously, but is called on a future tick of the event loop. That means that the rest of your synchronous code always runs before any .then() handler is called.
So, thus your doOther() function runs before either doSuccess() or doError() are called.
Promises are designed this way so that a promise .then() handler will be called with consistent timing whether the promise is resolved immediately or resolved some time in the future. If synchronous .then() handlers were allowed, then calling code would either have to know when it might get called synchronously or you'd be susceptible to weird timing bugs.
In the Promises/A+ specification which the promises in the ES6 specification were based on, it defines a `.then() handler like this:
promise.then(onFulfilled, onRejected)
and then has this to say about it:
2.2.4. onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].
And, then it defines platform code like this:
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.
Basically what this means is that .then() handlers are called by inserting a task in the event loop that will not execute until the currently running Javascript finishes and returns control back to the interpreter (where it can retrieve the next event). So, thus any synchronous Javascript code you have after the .then() handler is installed will always run before the .then() handler is called.
I had similar confusions about what it means by executing after the current loop. I was going through MDN docs on Promises, which says the following:
Callbacks added with then() will never be invoked before the completion of the current run of the JavaScript event loop.
This website http://latentflip.com/loupe/ video expains it pretty well, basically api's like setTimeout execute after the inbuilt js functions. But, the problem with callbacks is, if it doesn't use those apis then it might execute before the finish of current run of the event loop. Following examples show the difference:
Old school callbacks
var foo = function(then1) {
console.log("initial");
var i = 0;
while (i < 1000000000) {
i++;
}
then1();
}
function then11() {
console.log("res");
}
foo(then11);
console.log("end"); // unlike Promises, end is printed last
New Promise
var promise = new Promise(function(resolve, reject) {
console.log("Initial");
//while loop to 10000
var i = 0;
while(i<1000000000) { //long async task without ext apis
i++;
}
resolve("res");
});
promise.then(function(result) {
console.log(result); // "Stuff worked!"
});
console.log("end"); // end is printed before res!!!
In this example, I want to return a pending "stop" Promise (1) which deletes an instance's reference to itself (1), and which callee may have pending / queued actions on.
In the case that a stop is currently happening, I want to return that existing, pending promise.
My question is whether or not the initial condition is deterministic and that it will always return a Promise; not undefined.
Since the variable / reference is removed upon completion, I'm curious if an async action could "jump in" between the conditional and the return statement, or if this is forbidden by block execution / precedence.
Thanks
stop () {
if (this.awaitStop) {
return this.awaitStop;
} else {
this.awaitStop = NativeDevice.video.stop(); // Promise
return this.awaitStop.then(() => delete this.awaitStop);
}
}
Non-async functions do not get interrupted. Any callbacks will only be executed after all functions have returned and control flow returns to the "event loop".
Things would be different if you had an async function:
async function f() {
if (foo) {
await bar(); // This interrupts f and lets any other code run.
console.log(foo); // foo may or may not be the same as before.
}
}
JavaScript has Run-To-Completion semantic, and is not multi-threaded in the conventional manner, as a standalone language (see here for examples where this could break depending on the environment).
What Run-To-Completion means for this example is that all synchronous code will run until finished and cannot be interrupted by any async call.
Only generators (with yield) and async functions (with await) can cause interuptions in JS and this has to be done explicitly (non-preemptive multitasking).
Since you do not have either in your code, your conditional will behave deterministically.
I would like to get a deeper understanding of how Promises work internally.
Therefore I have some sample code:
var p1 = new Promise(
function(resolve, reject) {
window.setTimeout(
function() {
resolve('res called')
}, 2000);
});
var p2 = new Promise(
function(resolve, reject) {
window.setTimeout(
function() {
resolve('res called')
}, 2000);
});
function chainPromises() {
return p1.then(function(val) {
console.log("p1");
return p2.then(function(val) {
console.log("p2");
return val;
});
});
}
chainPromises().then(function(val) {
console.log(val);
});
Here a link to execute this code.
As you would predict, first p1 is resolved, afterwards p2 and in the end the final then prints the resolv value.
But the API ref states the following:
"then" returns a new promise equivalent to the value you return from
onFulfilled/onRejected after being passed through Promise.resolve
So it would be interesting to know WHEN exactly the "then" function is executed?
Because the final "then" in the code is chained to the chainPromises(), I first thought that
it would execute after the function chainPromises() returns something (in this case another promise).
If this would have been the case the "val" of the final "then" function would be the returned promise.
But instead, the final "then" waits until all promises inside the first "then" which are returned have been resolved.
This absolutely makes sense because in this way, the "then" functions can be stacked, but
I do not really get how this is done, since the API spec. does not really cover what "then" returns and when the "then" functions is executed.
Or in other words, why does the final "then" function wait until all the Promises are resolved inside the chainPromises() function instead of just waiting for the first returned object as the API doc says.
I hope I could make clear what I mean.. :)
About Promise resolution
The thing you're witnessing here is called recursive thenable resolution. The promise resolution process in the Promises/A+ specification contains the following clause:
onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x)
The ES6 promise specification (promises unwrapping) contains a similar clause.
This mandates that when a resolve operation occurs: either in the promise constructor, by calling Promise.resolve or in your case in a then chain a promise implementation must recursively unwrap the returned value if it is a promise.
In practice
This means that if onFulfilled (the then) returns a value, try to "resolve" the promise value yourself thus recursively waiting for the entire chain.
This means the following:
promiseReturning().then(function(){
alert(1);
return foo(); // foo returns a promise
}).then(function(){
alert(2); // will only run after the ENTIRE chain of `foo` resolved
// if foo OR ANY PART OF THE CHAIN rejects and it is not handled this
// will not run
});
So for example:
promiseReturning().then(function(){
alert(1);
return Promise.resolve().then(function(){ throw Error(); });
}).then(function(){
alert("This will never run");
});
And that:
promiseReturning().then(function(){
alert(1);
return Promise.resolve().then(function(){ return delay(2000); });
}).then(function(){
alert("This will only run after 2000 ms");
});
Is it a good idea?
It's been the topic of much debate in the promises specification process a second chain method that does not exhibit this behavior was discussed but decided against (still available in Chrome, but will be removed soon). You can read about the whole debate in this esdiscuss thread. This behavior is for pragmatic reasons so you wouldn't have to manually do it.
In other languages
It's worth mentioning that other languages do not do this, neither futures in Scala or tasks in C# have this property. For example in C# you'd have to call Task.Unwrap on a task in order to wait for its chain to resolve.
Let's start with an easy perspective: "chainPromises" returns a promise, so you could look at it this way:
// Do all internal promises
var cp = chainPromises();
// After everything is finished you execute the final "then".
cp.then(function(val) {
console.log(val);
});
Generally speaking, when returning a promise from within a "then" clause, the "then" function of the encapsulating promise will be marked as finished only after the internal "then" has finished.
So, if "a" is a promise, and "b" is a promise:
// "a"'s "then" function will only be marked as finished after "b"'s "then" function has finished.
var c = a.then(function () {
return b.then(function () {
console.log("B!");
};
};
// c is a promise, since "then" always returns a promise.
c.then(function() {
console.log("Done!");
};
So the output will be:
B!
Done!
Notice btw, that if you don't "return" the internal promise, this will not be the case:
// "a"'s "then" function will only be marked as finished without waiting for "b"'s "then" to finish.
var c = a.then(function () {
// Notice we're just calling b.then, and don't "return" it.
b.then(function () {
console.log("B!");
};
};
// c is a promise, since "then" always returns a promise.
c.then(function() {
console.log("Done!");
};
Here we can't know what would be outputted first. It could be either "B!" or "Done!".
Please check the below example regarding how promises works:
The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.
console.log('person1: shoe ticket');
console.log('person2: shoe ticket');
const promiseGirlFriendBringingTickets = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ticket');
}, 3000);
});
promiseGirlFriendBringingTickets.then((t) => {
console.log(`person3: show ${t}`);
})
console.log('person4: shoe ticket');
console.log('person5: shoe ticket');
Promise then return promise object, not promise's resolved value. I forked your JsFiddle, and added some of mine try this.
promise.then is executed right after that promise object is resolved.
I do not know how this is done in actual promises libraries, but I was able to re-create this functionality in the following way:
1) each promise has a waitingPromises property;
2) then method returns a new promise, and the original promise's waitingPromises property points to the new promise.
In this way, the chain of .then()s creates a structure that is similar to a linked list or rather a tree (each promise can have several waiting promises). A promise can be resolved only after its 'parent' promise has been resolved. The .then method itself is executed immediately, but the corresponding promise that it creates is resolved only later.
I am not sure this is a good explanation and would love to learn about other possible approaches.
Normally code is synchronous - one statement executes like (fileopen) and there is a guarantee that the next statement will execute immediately afterwards like filewrite()
but in asynchronous operations like nodejs, you should assume that
you have no idea when the operation will complete.
You can't even assume that just because you send out one request first, and another request second, that they will return in that order
Callbacks are the standard way of handling asynchrnous code in JavaScript
but promises are the best way to handle asynchronous code.
This is because callbacks make error handling difficult, and lead to ugly nested code.
which user and programmer not readble easily so promises is the way
You can think of Promise as a wrapper on some background task. It takes in a function which needs to be executed in the background.
The most appropriate place to use a promise is where some code is dependent on some background processing and it needs to know the status of the background task which was executed. For that, the background task itself accepts two callback resolve and reject in order to convey its status to the code which is dependent on it. In layman terms, this code is the one behind it in the promise chain.
When a background task invokes resolve callback with some parameter. it's marking the background operation successful and passing the result of the background operation to the next then block which will be executed next. and if it calls reject, marking it as unsuccessful then the first catch block will be executed.
In your custom promise, you can pass an error obj to the reject callback so that next catch block is aware of the error happened in the background task.
I'm relatively new to JavaScript programming, and so callbacks have been giving me trouble. I'm using a framework that supports the "async/await" syntax from ES7 (Meteor), but I'm having some difficulty understanding exactly how I can implement an asynchronous function that calls other functions.
What I want to do: The user enters a URL, data is fetched from the URL, processed, and put into a database. I want this to occur in a non-blocking manner, so that the user can continue to use the application while this process is occurring.
Pseudocode:
async function foo(URL) {
try {
//using request-promise npm package to make the request
const data = await makeRequestPromise(URL)
const parsedData = await myParsingFunciton(data);
MyDB.insert({
parsedData,
});
catch (err) {
console.log(err);
}
So I have a few questions:
Is anything that happens in the scope of this async function non-blocking?
If so, can I just use a series of synchornous functions since I can't parse the data before I receive it anyway?
What happens if I try to await a function that is not defined as asynchronous (for example myParsingFunction)? My research seems to imply that the function is forced to return a promise, but I'm not certain how I would determine that. Doing so in the code does not return an error, but JavaScript seems surprisingly tolerant of weirdness with regards to returns.
I would like to add that my function works and things go into the database, but I have no idea how to test if it is actually non-blocking.
Is anything that happens in the scope of this async function non-blocking?
It's non-blocking in the sense that the call to foo won't stall the thread until all the work is done, because foo is an asynchronous function calling at least one other asynchronous function. There's only one main UI thread on browsers (and one thread in NodeJS), so unless you use web workers in the browser, the asynchronous code will at some point be occupying that thread (e.g., blocking). But the asynchronous functions you call within foo won't block the thread within foo; instead, foo returns a promise behind the scenes that gets resolved eventually after all the async work is done. (That's why it's an asynchronous function.)
If so, can I just use a series of synchornous functions since I can't parse the data before I receive it anyway?
Not quite following this question. Your code looks fine. It looks synchronous, but it isn't, assuming one or more of the functions you're calling is asynchronous.
What happens if I try to await a function that is not defined as asynchronous (for example myParsingFunction)?
It makes your call to that function asynchronous (even though the function isn't) but then the resolution happens as soon as possible thereafter; the resolution value is the value returned by the function. You can see that in action with this quick experiment:
// `foo` is an asynchronous function, and so calling it returns a promise
async function foo() {
// We haven't awaited anything yet, so this is run synchronously when we
// call `foo`
console.log("A");
// `Math.random` is, of course, a synchronous function; but we *call* it
// asynchronously since we've used `await`
let data = await Math.random();
// Which means this line doesn't run until later, after `foo` has already
// returned its promise
console.log("B");
}
// When we call `foo`, the synchronous part runs first and it returns its
// promise (which we're not using)
foo();
// And then this line runs, BEFORE the asynchronous part of `foo` can run
console.log("C");
// ...and so we see A, C, B; without the `await` on the call to
// `Math.random`, we'd see A, B, C
It may (or may not!) be useful to remember that async/await is purely syntactic sugar for interacting with promises. async means the function returns a promise. await means you're waiting (asynchronously) for a promise resolution.
E.g.:
async function foo() {
return 42;
}
is sugar for
function foo() {
return new Promise(resolve => {
resolve(42);
});
}
and sugar for
let data = await something();
// ...do somthing...
is
something().then(data => {
// ...do something...
});
leaving aside some minor details.