I'd like to understand the promises chaining in Javascript. So I wrote this little fiddle:
https://jsfiddle.net/GarfieldKlon/89syq4fh/
It works like intended. c waits for b, and b waits for a.
But when I change these lines to:
a().then( result => console.log(result))
.then( () => {b().then( result => console.log(result))})
.then( () => {c().then( result => console.log(result))});
So I added the curly braces around b and c and then the output is 1, 3, 2. And I don't understand why.
When I add a return then it works again:
a().then( result => console.log(result))
.then( () => {return b().then( result => console.log(result))})
.then( () => {return c().then( result => console.log(result))});
But why? When there are no curly braces you're only allowed to write one statement, correct? And this statement is implicitly returned?
And when you use curly braces you have to explicitly return something?
But why does it mess up the order when using curly braces without return? And why does it still log something when the return is missing?
When there are no curly braces you're only allowed to write one expression which is implicitly returned?
And when you use curly braces you have to explicitly return something?
Yes.
But why does it mess up the order when using curly braces without return? And why does it still log something when the return is missing?
Because the promise then function relies on return values for chaining.
Remember that then returns a new promise for the result of the callback. When that result is another promise, it waits for the result of that inner promise before fulfilling the returned promise - the one on which you are chaining the second then() call.
If your callback starts b().then(…) but returns undefined, the next step in the chain (the c() call) doesn't wait for b to finish.
When using the arrow syntax, you can interpret it in many things:
() => 'hello'
is equivalent to
() => { return 'hello'; }
However, when doing
() => {'hello'}
as similar to what you wrote:
.then( () => {b().then( result => console.log(result))})
then you can interpret that as
() => {'hello'; return;}
So, in the code, yours promise handling b().then( result => console.log(result)) is called before a void return; is done. You are not returning the resulting promise object at all. It gets called when b is finished, no matter of what you were doing.
This has to do with arrow functions. When you omit the curly braces, the return is implicit. So
const f = () => 42;
is equivalent to
const f = () => { return 42; };
Also, b().then(something).then(somenthingElse) is still only one expression, so it can be implicitly returned.
A Promise is an object that represents the result of an asynchronous operation.
It is important to remember that Promises are not just abstract ways registering callbacks to run when some async code finishes — they represent the results of that async code.
When we write a chain of .then() invocations, we are not registering multiple callbacks on a single Promise object. Instead, each invocation of the then() method returns a new Promise object.
That new Promise object is not fulfilled until the function passed to then() is complete. The value that fulfills promise 1 becomes the input to the callback2() function. The callback performs some computation and returns a value v. When the callback returns the value, the promise is resolved with the value v.
When a Promise is resolved with a value that is not itself a Promise, it is immediately fulfilled with that value.
It is important to understand in this case, so I rephrase: if a callback returns a non-Promise, that return value becomes the value of the promise, and the promise is fulfilled.
If the return value v is itself a Promise, then the promise is resolved but not yet fulfilled.
Your case:
allow me to post the functions a,b and c here:
let a = function() {
return new Promise( (resolve, reject) => {
setTimeout(() => resolve(1), 300);
});
}
let b = function() {
return new Promise( (resolve, reject) => {
setTimeout(() => resolve(2), 200);
});
}
let c = function() {
return new Promise( (resolve, reject) => {
setTimeout(() => resolve(3), 100);
});
}
and the implementation
a().then( result => console.log(result))
.then( () => {b().then( result => console.log(result))})
.then( () => {c().then( result => console.log(result))});
Here is what is happening
By adding the curly braces to the arrow function body, we no longer get the automatic return.
This function now returns undefined instead of returning a Promise, which means that the current Promise is fulfilled and next stage in this Promise chain will be invoked with undefined as its input.
So, function a gets invoked, returns a promise, which is, once fulfilled invokes the callback1.
callback 1 returnes undefined so the promise1 is fulfilled and the next .then() invokes the callback2.
The callback2 invokes the b which itself returnes a Promise, but this Promise is not returned by the callback2. The callback2 returns undefined. The Promise2 is fulfilled with undefined and life goes on - Callback3 gets invoked.
At this point b and c are running simultaneously, and because c is 100ms faster than b you get the 1,3,2 order.
Terminology
We say that the promise has been fulfilled if and when the first callback (argument of then method) is called.
And we say that the Promise has been rejected if and when the second callback (argument of then method) is called.
If a Promise is neither fulfilled nor rejected, then it is pending. And once a promise is fulfilled or rejected, we say that it is settled.
If the async code runs normally (and the Promise is fulfilled), then that result is essentially the return value of the code.
If the Promise is fulfilled, then the value is a return value that gets passed to any callback functions registered as the first argument of then().
Promises can also be resolved. It is easy to confuse this resolved state with the fulfilled state or with settled state, but it is not precisely the same as either.
Resolved means that another Promise has been assigned to the value of the current Promise. The current Promise has become associated with, or “locked onto,” another Promise.
We don’t know yet whether the Promise will be fulfilled or rejected, because it is not yet settled. Its fate now depends entirely on what happens to Promise
Related
Please help me to understand the following behavior of the Promise class. I have spend a lot of time reading instructions and documentation but can't figure this out by myself.
const promise = new Promise(resolver=>
setTimeout(resolver("i am resolver"), 1000)
)
.then(resolvedParameter => el => {console.log(el); console.log(resolvedParameter)})
.then(upperFunc => upperFunc("log me this"))
.then(() => console.log("do not care about the others"))
.catch(e => (error = e));
promise.then(()=>console.log("test"));
This is the console output:
log me this
i am resolver
do not care about the others
test
In the Promise where setTimeout is used no return value was specified, jet the string "i am resolver" was returned and used in the first then(). I presume that when you call resolver (as a promise resolver function) it returns the passed parameter for the next then()
In the first then() the resolvedParameter is passed in function that returns a function that will be returnd and then used in the second then() which is logical, and the string from the first promise is remembered ("i am resolver") and used correctly.
Can you make a then() function with resolver and rejection function ? Please help me understand what ways do we have available to pass data from first promise to the next function then() and what is expected to be returned from the then() function. (I read and understood that they are new Promises but still the behavior is a bit hard to understand)
Thank you for reading this question and any insight is welcome.
For background information, here are different ways to make a resolved promise:
promise = new Promise(resolver => resolver('any value'));
promise = Promise.resolve('any value');
promise = Promise.resolve().then(() => 'any value');
promise = (async () => 'any value')()
They are functionally equivalent. They all return a promise that is resolved with the value 'any value'.
.then() function gets, as the first argument, whatever is resolved previously up the chain and can return three different ways:
If it returns a normal value, that is passed down the chain as the resolved value.
If it throws a value, that value is passed down the chain as rejection.
If it returns a promise[1], then what ever that promise resolves or rejects as is passed down the chain as resolved value or rejection.
All your examples fall into the first category. Here is a list of values passed down in the resolved promises:
"i am resolver"
el => {console.log(el); console.log(resolvedParameter)}
undefined // because that is what the above function returns when called
undefined // because that is what the console.log returns
undefined // again console.log return value
Here is an example where I have wrapped all your returns values inside promises:
const promise = new Promise(resolver=>
resolver(Promise.resolve("i am resolver"));
)
.then(resolvedParameter => Promise.resolve(el => {console.log(el); console.log(resolvedParameter)}))
.then(upperFunc => Promise.resolve(upperFunc("log me this")))
.then(() => Promise.resolve(console.log("do not care about the others")))
.catch(e => Promise.resolve(error = e));
promise.then(()=>Promise.resolve(console.log("test")));
This functions identically to your example, but hopefully it is clear now what is passed down the chain.
As others have mentioned, the second argument of .then() is called when rejection is passed from the chain. It works the same as the .catch() method. .then(undefined, someFunc) is same as .catch(someFunc).
[1] Strictly, it can also be a "thenable", something that has a then method, but that is a special case.
for a while, I thought the only way to chain .then() statements was to return a promise from the .then method. e. g.:
asyncFunc()
.then(res => asyncFunc2()) // returns a promise
.then(res => doSmth())
But now I figured out that you can actually also return a plain old value from the .then function and it also works:
const build = () => new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Hallo")
}, 100)
})
build()
.then(res => {
console.log(res);
return res + " I'm";
})
.then(res => {
console.log(res + " Peter");
})
Is there a difference between the two in terms of how and when they are executed?
Each .then() method call returns a promise, if the return value of the callback function is also a promise, then the promise returned by the .then() method call gets resolved to that promise, meaning its fate will now depend on what happens to the promise returned by the callback function. It will settle when the promise returned by the callback function settles.
On the other hand, if you return a non-promise value, then that value is implicitly wrapped in a promise and passed to the next .then() method call, if there's any. If you don't return anything from the callback function, the promise returned by the .then() method will resolve with the value of undefined.
In first code example, as you are returning asyncFunc2() from the callback function of first .then() method, the promise returned by it will get resolved to the promise returned by asyncFunc2().
In the second code example, res + " I'm" will be implicitly wrapped in a promise and then passed to second then() method.
I was learning asynchrony in JS and came across Async/Await. Then, I came across this code:
function scaryClown() {
return new Promise(resolve => {
setTimeout(() => {
resolve('🤡');
}, 2000);
});
}
async function msg() {
const msg = await scaryClown();
console.log('Message:', msg);
}
msg(); // Message: 🤡 <-- after 2 seconds
Thus, I have questions on the above code. Firstly, how can async function msg() return message value if the function itself returns only undefined Promise, that is, the function does not explicitly use return keyword. Secondly, does await returns Promise or value itself having been unwrapped from Promise?
function scaryClown() {
return new Promise(resolve => {
setTimeout(() => {
resolve('🤡');
}, 2000);
});
}
The above function is a function which returns a promise when it is called, but will only be resolved after 2 seconds.
async function msg() {
const msg = await scaryClown();
console.log('Message:', msg);
}
the above function is asynchronous function which awaits for the promise to be resolved (in your case, after 2 seconds) and then only the console.log() triggers.
Note: any lines of code below the function msg() gets executed irrespective of the promise and the asynchronous function.
Running code snippet
function scaryClown() {
return new Promise(resolve => {
setTimeout(() => {
resolve('🤡');
}, 2000);
});
}
async function msg() {
console.log(scaryClown()); // try without await, so it will not wait for the result and prints first, but an unresolved promise
const msg = await scaryClown(); // actual result which is resolved, because we awaited
console.log('Message:', msg); // Prints once the resolved value is available.
}
msg(); //executes first but only prints the value once it is resolved.
console.log('I will continue executing irrespective of async func msg()');
async function msg() {
const msg = await scaryClown();
console.log('Message:', msg);
}
await resolves promise and return resolved value. so above code with then can be written like below. hope this makes you understand async await.
function msg() {
scaryClown().then(msg => {
console.log('Message:', msg);
});
}
This is what happens:
When the async function (i.e. msg) is invoked, it creates a promise object (let's call it P), which it will return when it deals with an await expression. scaryClown() executes, which sets the timer and returns yet another promise object; let's call it Q. Then await is processed: at that moment the function returns the promise P. This promise is not resolved at that moment; it is pending.
If then there is any other code that followed the call of msg(), then that is executed like would be the case after any function call. Execution continues until the call stack is empty.
Then, some time later the timer event that was scheduled by scaryClown() will fire, and the corresponding promise Q resolves. Because this promise Q was in an await expression, this resolution triggers a restoration of the execution context of msg(), where the await expression is now evaluated to be Q's resolved value, i.e. "🤡".
The variable msg gets this value assigned, and it is displayed with console.log. Then the function completes, in this particular case without any explicit return. Remember that the function had already returned, so this is not really a normal return: code execution really ends here, as there is nothing on the call stack. This makes the promise P resolve. If there had been an explicit return statement, with a value, the promise P would have been resolved with that value. But since there is no such return statement in your example, promise P resolves with undefined.
As you don't do anything with the value returned by msg(), you don't really see what it returns. If you would have done:
msg().then(console.log);
... you would have seen undefined in the console.
Your questions:
how can async function msg() return message value if the function itself returns only undefined Promise, that is, the function does not explicitly use return keyword.
The msg() call does currently not provide you with the message value. Instead it uses console.log to output it, but that is not the value returned by msg(). The returned value is a promise. Even if it had an explicit return keyword, it would still not make msg() return something else than a promise. The only difference would be what value that promise would eventually resolve to.
does await returns Promise or value itself having been unwrapped from Promise?
await does two things at two different moments in time. First it makes the function return to the caller. Then, when the promise that it awaits resolves, it makes the function continue to run again, and await represents at that moment the promised value, which can therefore be assigned to a variable (or used in another way).
I personally don't use the verb "unwrapped" when talking about promises. But yes, the expression await promise evaluates (asynchronously) to the value that promise fulfils to.
I think one principle I take so far is:
A promise is a thenable object, and so it takes the message then, or in other words, some code can invoke the then method on this object, which is part of the interface, with a fulfillment handler, which is "the next step to take", and a rejection handler, which is "the next step to take if it didn't work out." It is usually good to return a new promise in the fulfillment handler, so that other code can "chain" on it, which is saying, "I will also tell you the next step of action, and the next step of action if you fail, so call one of them when you are done."
However, on a JavaScript.info Promise blog page, it says the fulfillment handler can return any "thenable" object (that means a promise-like object), but this thenable object's interface is
.then(resolve, reject)
which is different from the usual code, because if a fulfillment handler returns a new promise, this thenable object has the interface
.then(fulfillmentHandler, rejectionHandler)
So the code on that page actually gets a resolve and call resolve(someValue). If fulfillmentHandler is not just another name for resolve, then why is this thenable different?
The thenable code on that page:
class Thenable {
constructor(num) {
this.num = num;
}
then(resolve, reject) {
alert(resolve); // function() { native code }
// resolve with this.num*2 after the 1 second
setTimeout(() => resolve(this.num * 2), 1000); // (**)
}
}
new Promise(resolve => resolve(1))
.then(result => {
return new Thenable(result); // (*)
})
.then(alert); // shows 2 after 1000ms
A thenable is any object containing a method whose identifier is then.
What follows is the simplest thenable one could write. When given to a Promise.resolve call, a thenable object is coerced into a pending Promise object:
const thenable = {
then() {}, // then does nothing, returns undefined
};
const p = Promise.resolve(thenable);
console.log(p); // Promise { <pending> }
p.then((value) => {
console.log(value); // will never run
}).catch((reason) => {
console.log(reason); // will never run
});
The point of writing a thenable is for it to get coerced into a promise at some point in our code. But a promise that never settles isn't useful. The example above has a similar outcome to:
const p = new Promise(() => {}); //executor does nothing, returns undefined
console.log({ p }); // Promise { <pending> }
p.then((value) => {
console.log(value); // will never run
}).catch((reason) => {
console.log(reason); // will never run
});
When coercing it to a promise, JavaScript treats the thenable's then method as the executor in a Promise constructor (though from my testing in Node it appears that JS pushes a new task on the stack for an executor, while for a thenable's then it enqueues a microtask).
A thenable's then method is NOT to be seen as equivalent to promise's then method, which is Promise.prototype.then.
Promise.prototype.then is a built-in method. Therefore it's already implemented and we just call it, passing one or two callback functions as parameters:
// Let's write some callback functions...
const onFulfilled = (value) => {
// our code
};
const onRejected = (reason) => {
// our code
};
Promise.resolve(5).then(onFulfilled, onRejected); // ... and pass both to a call to then
The executor callback parameter of a Promise constructor, on the other hand, is not built-in. We must implement it ourselves:
// Let's write an executor function...
const executor = (resolve, reject) => {
// our asynchronous code with calls to callbacks resolve and/or reject
};
const p = new Promise(executor); // ... and pass it to a Promise constructor
/*
The constructor will create a new pending promise,
call our executor passing the new promise's built-in resolve & reject functions
as first and second parameters, then return the promise.
Whenever the code inside our executor runs (asynchronously if we'd like), it'll have
said promise's resolve & reject methods at its disposal,
in order to communicate that they must respectivelly resolve or reject.
*/
A useful thenable
Now for a thenable that actually does something. In this example, Promise.resolve coerces the thenable into a promise:
const usefulThenable = {
// then method written just like an executor, which will run when the thenable is
// coerced into a promise
then(resolve, reject) {
setTimeout(() => {
const grade = Math.floor(Math.random() * 11)
resolve(`You got a ${grade}`)
}, 1000)
},
}
// Promise.resolve coerces the thenable into a promise
let p = Promise.resolve(usefulThenable)
// DO NOT CONFUSE the then() call below with the thenable's then.
// We NEVER call a thenable's then. Also p is not a thenable, anyway. It's a promise.
p.then(() => {
console.log(p) // Promise { 'You got a 9' }
})
console.log(p) // Promise { <pending> }
Likewise, the await operator also coerces a thenable into a promise
console.log('global code has control')
const usefulThenable = {
// then() will be enqueued as a microtask when the thenable is coerced
// into a promise
then(resolve, reject) {
console.log("MICROTASK: usefulThenable's then has control")
setTimeout(() => {
console.log('TASK: timeout handler has control')
const grade = Math.floor(Math.random() * 11)
resolve(`You got a ${grade}`)
}, 1000)
},
}
// Immediately Invoked Function Expression
let p = (async () => {
console.log('async function has control')
const result = await usefulThenable //coerces the thenable into a promise
console.log('async function has control again')
console.log(`async function returning '${result}' implicitly wrapped in a Promise.resolve() call`)
return result
})()
console.log('global code has control again')
console.log({ p }) // Promise { <pending> }
p.then(() => {
console.log('MICROTASK:', { p }) // Promise { 'You got a 10' }
})
console.log('global code completed execution')
The output:
/*
global code has control
async function has control
global code has control again
{ p: Promise { <pending> } }
global code completed execution
MICROTASK: usefulThenable's then has control
TASK: timeout handler has control
async function has control again
async function returning 'You got a 10' implicitly wrapped in a Promise.resolve() call
MICROTASK: { p: Promise { 'You got a 10' } }
*/
TL;DR: Always write the thenable's then method as you would the executor parameter of a Promise constructor.
In
let p2 = p1.then( onfulfilled, onrejected)
where p1 is a Promise object, the then call on p1 returns a promise p2 and records 4 values in lists held internally by p1:
the value of onfulfilled,
the value of the resolve function passed to the executor when creating p2 - let's call it resolve2,
the value of onrejected,
the value of the reject function passed to the executor when creating p2 - let's call it reject2.
1. and 3. have default values such that if omitted they pass fulfilled values of p1 on to p2, or reject p2 with the rejection reason of p1 respectively.
2. or 4. (the resolve and reject functions for p2) are held internally and are not accessible from user JavaScript.
Now let's assume that p1 is (or has been) fullfilled by calling the resolve function passed to its executor with a non thenable value. Native code will now search for existing onfulfilled handlers of p1, or process new ones added:
Each onfulfilled handler (1 above) is executed from native code inside a try/catch block and its return value monitored. If the return value, call it v1, is non-thenable, resolve for p2 is called with v1 as argument and processing continues down the chain.
If the onfulfilled handler throws, p2 is rejected (by calling 4 above) with the error value thrown.
If the onfulfilled handler returns a thenable (promise or promise like object), let's call it pObject, pObject needs to be set up pass on its settled state and value to p2 above.
This is achieved by calling
pObject.then( resolve2, reject2)
so if pObject fulfills, its non-thenable success value is used to resolve p2, and if it rejects its rejection value is used to reject p2.
The blog post defines its thenable's then method using parameter names based on how it is being used in the blog example (to resolve or reject a promise previously returned by a call to then on a native promise). Native promise resolve and reject functions are written in native code, which explains the first alert message.
After writing down the whole explanation, the short answer is: it is because the JS promise system passed in a resolve and reject as the fulfillmentHandler and rejectionHandler. The desired fulfillmentHandler in this case is a resolve.
When we have the code
new Promise(function(resolve, reject) {
// ...
}).then(function() {
return new Promise(function(resolve, reject) {
// ...
});
}).then(function() { ...
We can write the same logic, using
let p1 = new Promise(function(resolve, reject) {
// ...
});
let p2 = p1.then(function() {
let pLittle = new Promise(function(resolve, reject) {
// ...
resolve(vLittle);
});
return pLittle;
});
The act of returning pLittle means: I am returning a promise, a thenable, pLittle. Now as soon as the receiver gets this pLittle, make sure that when I resolve pLittle with value vLittle, your goal is to immediately resolve p2 also with vLittle, so the chainable action can go on.
How does it do it?
It probably has some code like:
pLittle.then(function(vLittle) { // ** Our goal **
// somehow the code can get p2Resolve
p2Resolve(vLittle);
});
The code above says: when pLittle is resolved with vLittle, the next action is to resolve p2 with the same value.
So somehow the system can get p2Resolve, but inside the system or "blackbox", the function above
function(vLittle) {
// somehow the code can get p2Resolve
p2Resolve(vLittle);
}
is probably p2Resolve (this is mainly a guess, as it explains why everything works). So the system does
pLittle.then(p2Resolve);
Remember that
pLittle.then(fn)
means
passing the resolved value of pLittle to fn and invoke fn, so
pLittle.then(p2Resolve);
is the same as
pLittle.then(function(vLittle) {
p2Resolve(vLittle)
});
which is exactly the same as ** Our goal** above.
What it means is, the system passes in a "resolve", as a fulfillment handler. So at this exact moment, the fulfillment handler and the resolve is the same thing.
Note that in the Thenable code in the original question, it does
return new Thenable(result);
this Thenable is not a promise, and you can't resolve it, but since it is not a promise object, that means that promise (like p2) is immediately resolved as a rule of what is being returned, and that's why the then(p2Resolve) is immediately called.
So I think this count on the fact that, the internal implementation of ES6 Promise passes the p2Resolve into then() as the first argument, and that's why we can implement any thenable that takes the first argument resolve and just invoke resolve(v).
I think the ES6 specification a lot of time writes out the exact implementation, so we may somehow work with that. If any JavaScript engine works slightly differently, then the results can change. I think in the old days, we were told that we are not supposed to know what happens inside the blackbox and should not count on how it work -- we should only know the interface. So it is still best not to return a thenable that has the interface then(resolve, reject), but to return a new and authentic promise object that uses the interface then(fulfillmentHandler, rejectionHandler).
I'd like to have a promise to a promise, something like this:
let first = new Promise(resolveFirst => {
setTimeout(() => {
resolveFirst("resolved!");
}, 2000)
});
let second = new Promise(resolveSecond => {
setTimeout(() => {
resolveSecond(first);
}, 10)
});
second.then(secondValue => {
console.log("second value: ", secondValue);
secondValue.then(firstValue => {
console.log("first value: ", firstValue);
});
});
So that the console.log("second value: ", secondValue); is printed after 10 millies and then the console.log("first value: ", firstValue) will be printed 2000 afterwards.
What happens though is that I get:
second value: resolved!
And an error:
Uncaught (in promise) TypeError: secondValue.then is not a function
Together after 2010 millies.
It seems that when the second promise is resolved, and the first one is returned then it automatically waits for this first promise to resolve as well.
Why is that? And how do I break between them?
Edit
Here's a solution that was posted on facebook using Array.reduce():
const runPromisesInSeries =
ps => ps.reduce((p, next) =>
p.then(next), Promise.resolve());
const delay = d => new Promise(r => setTimeout(r, d));
runPromisesInSeries([() => delay(1000), () => delay(2000)]);
//executes each promise sequentially, taking a total of 3 seconds to complete
(https://www.facebook.com/addyosmaniofficial/photos/a.10151435123819601.1073741825.129712729600/10155386805609601/?type=3&theater)
By design, a promise that is resolved with a second promise takes on the value and state of the 2nd promise after the 2nd promise becomes settled (fulfilled or rejected), waiting for it to be settled as necessary.
It is not possible to fulfill a promise with a promise or thenable object (any object with a then method`) value unless the promise library is seriously flawed (older versions of JQuery would fulfill their own promise objects with a promise object from a different library).
Promise rejection does not have the same checks in place and will happily pass a promise object down the chain without waiting for it to be resolved. So you could reject the 2nd promise with the first and pick it up in a catch clause.
Although technically feasible I strongly advise against doing so except to prove it can be done - it's a maintenance issue for a third party trying to understand the code.
While you could pass the promise as an object property down the success channel of a promise chain, re-analysis of promise composition may provide a better or more standard solution. E.G. Promise.all waits for for two or more independent promises to be fulfilled before proceeding on a common task.
That's just part of their charm: If your promise A resolves to a thenable B, then A resolves only after B resolves, and A takes B's resolved value. That's part of both Promises/A+ and ES6, as part of the "promise resolution procedure".
You can see some of the advantages of that if (say) one action requires an action before its completion (like login), or requires loading another page of results, or has retry logic of its own.
Though it's not very idiomatic, if you want to return an unresolved promise immediately without waiting for it, you can do so by passing it in an {object} or [array], but there might not be much reason for that: Other than waiting for its completion, what would you do with a returned promise?
You can chain then only to a promise.
secondValue is not a promise, it's just the value that returned when you called the second promise. or the resolved value from that promise.
If you want that to work, try this:
let first = new Promise(resolveFirst => {
setTimeout(() => {
resolveFirst("resolved!");
}, 2000)
});
let second = new Promise(resolveSecond => {
setTimeout(() => {
resolveSecond(first);
}, 10)
});
second.then(secondValue => {
console.log("second value: ", secondValue);
/** first is a Promise. return a promise, and the next "then"
* will pass the result of that Promise.
*/
return first
})
.then(firstValue => {
console.log(firstValue)
})
EDIT 1
Further explanation for why second promise resolved the eventual value (string) of the first promise, and not the promise itself, can be found on mdn:
Promise.resolve()
if the value was a promise, that object becomes the result of the call to Promise.resolve; otherwise the returned promise will be fulfilled with the value.