Reputation: 27286
Given that the client programmer both defines and uses the resolve
function passed to the executor of a Promise, I naively assumed that I fully control the signature of the resolve
function and so I can define it to take more than one arguments.
Yet the following code (jsFiddle) fails:
<html>
<script>
const promise = new Promise(function executor(resolve, reject) {
setTimeout(function () {
resolve(42, true);
}, 1000);
});
promise.then(function resolveCallback(answer, howCertain) {
console.log('callback called with arguments: ('+answer+', '+howCertain+')');
console.log('the answer is: '+answer+'. I am '+(howCertain?'very':'not very')+' certain about that');
});
</script>
</html>
The above code actually prints on the console:
callback called with arguments: (42, undefined)
the answer is: 42. I am not very certain about that
Digging a little deeper, I wrote the below code (jsFiddle):
<html>
<script>
const promise = new Promise(function executor(resolve, reject) {
console.log('entering executor, resolve is: ['+resolve+']'.);
setTimeout(function () {
console.log('about to call resolve');
resolve(42);
console.log('returning from resolve');
}, 1000);
});
console.log('about to call "then"');
promise.then(function resolveCallback(answer) {
console.log('the answer is: '+answer);
});
</script>
</html>
which actually prints the following on the console:
entering executor, resolve is: [function t() { [native code] }]
about to call "then"
about to call resolve
returning from resolve
the answer is: 42
This demonstrates a number of interesting things:
then
on the promise. And yet, the resolve
parameter in the executor
function is not undefined
.resolveCallback
function that is passed as argument in the call to then
is not the same as the resolve
function passed to the executor
. Moreover, it is not executed synchronously (i.e. not in the same event loop run) with the resolve
call from within the executor
function.The only way for Javascript to pull this off (that I can think of) is that the implementation is actually plugging some sort of "proxy" resolve
and reject
functions which are asynchronously linked to the actual resolve
and reject
functions supplied by the client programmer. This would also explain why you can't define your own signature for the resolve
and reject
functions: because the plumbing won't work.
Is this mental model correct or am I missing something?
Upvotes: 4
Views: 1063
Reputation: 664920
The client programmer defines the resolve function passed to the executor of a Promise
No, he doesn't (as your experiment confirms). The executor is called during the new Promise
creation.
When I call
then
on the promise, don't I pass aresolve
function of my choosing?
No, you pass an onResolve
listener callback, or actually an onFulfill
callback.
Is my new mental model correct?
Yes, there is more plumbing below the surface.
Indeed, the new Promise
constructor is giving you just proxy resolver functions.
A promise contains a state machine. It is an object representing an (one!) asynchronous result. As such, you can call .then()
as many times on the same promise object, and the callbacks will always get passed the result.
It is not a wrapper around the executor
function, it does not call the executor as often as you call then
. The executor is called just once, to give the producer access to the resolver functions (the input of the state machine). The consumers that call then
, watching the output of the state machine, are completely decoupled from this - as you say, they are asynchronously linked.
Also have a look at this. For your mental model, this is an easy-to-understand implementation of the Promise
class (sans guaranteed asynchrony and error handling).
Upvotes: 5