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.
Related
I have abstraction:
function fetchDataFromAPI() {
const url = `https://api...`
return fetch(url).then(response => response.json())
}
I want to use it in my other piece of code like:
if(something){
const data = fetchDataFromAPI()
return data
}
if I console.log data what I get is resolved pending promise
Promise {<pending>}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: Object
How do I get that Object in data instead of Promise?
You can not. Here is why:
Promise is a language construct that makes JavaScript engine to continue to execute the code without waiting the return of inner function, also known as the executor function. A promise always run inside the event loop.
var p = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 300);
});
console.log(p);
Basically a promise is a glorified syntactic sugar for a callback. We will see how but first lets have a more realistic code:
function someApiCall(){
return new Promise(function(resolve, reject){
setTimeout(()=>{
resolve('Hello');
})
})
}
let data = someApiCall();
console.log(data);
This is a so-called asynchronous code, when JavaScript engine executes it, someApiCall immediately returns a result, in this case pending promise:
> Promise {<pending>}
If you pay attention to the executor, you will see we needed to pass resolve and reject arguments aka callbacks. Yes, they are callbacks required by the language construct. When either of them called, promise will change its state and hence be settled. We don't call it resolved because resolving implies successful execution but a function also can error out.
How do we get the data? Well we need more callbacks, which will be called by the executor function once the promise is settled:
var p = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 300);
});
p.then((result) => {
console.log(result); // foo
}).catch((err) => {
console.log(err);
});
Why we need to pass separate callbacks? Because one will be fed to the resolve, and the other to the reject. Then callback will be called by the resolve function, the catch callback by the reject function.
Javascript engine will execute these callbacks later on its leisure, for a regular function it means when the event loop is cleared, for timeout when the time is up.
Now to answer your question, how do we get data out from a promise. Well we can't.
If you look closely, you will see we don't really get the data out but keep feeding callbacks. There is no getting data out, but passing callbacks in.
p.then((result) => {
console.log(result);
}).catch((err) => {
console.log(err);
});
Some say use await:
async function() {
let result = await p;
}
But there is a catch. We have to or wrap it in async function. Always. Why? Because Async/await is another level of abstraction or syntactic sugar, whichever you prefer, on top of promise api. That is why we can not use await directly but always wrap it in async statement.
To sum up, when we use promise or async/await we need to follow certain convention and write terse code with closely knitted callbacks. Either javascript engine or transpilers like babeljs or typescript converts these code to regular javascript to be run.
I can understand your confusion because people keep saying getting data out when talking about promises, but we don't get any data out but pass callback to be executed when the data is ready.
Hope everything is clear now.
No, you cannot without using promises or async/await etc because calling a REST API is an asynchronous operation and is non blocking.
When you make a call to a REST API, the code shouldn't wait until the API returns a value because it may take a lot of time, making program non-responsive, thus by design making a network request is categorized as an asynchronous operation.
To avoid async/await, you'll need to use another .then:
if (something) {
return fetchDataFromAPI()
.then((data) => data /* you can console.log(data) here */)
}
I am new to asynchronous JavaScript, while learning Promise in JavaScript, I wrote a simple program
var p=new Promise(function(resolve,reject)
{
//Any async task.
var IsPromiseFulfilled=true;
if(IsPromiseFulfilled){
resolve("Promise Fulfilled");
}
else
{
reject("Promise Rejected");
}
});
p.then(function(status){
console.log(status);
}).catch(function(status){
console.log(status);
});
console.log("End Of Program");
The Output was:
End Of Program
Promise Fulfilled
Can anyone please tell me why "End Of Program" was printed earlier than "Promise Fulfilled"
Because it's executed outside of your promises and in turn executes before the promises resolve.
Although the Promise.resolve().then() call isn't slow, it will process all the code then start resolving the promises.
It's also worth noting you can shorten things a little as well by using promises like:
Promise.resolve().then(result => {
// do something with your result
}).catch(error => {
// do something with your error.
});
I find this format easier than using new Promise(function (resolve, reject) {})
It works this way because of the way .then() handlers are described in the promise specification.
All .then() handlers are executed on a future tick of the event loop. That means that any synchronous code executes before any .then() handler even if the promise is resolved immediately.
Here's a very simple example:
console.log("a");
Promise.resolve().then(() => {console.log("b");});
console.log("c");
This will output:
a
c
b
This is per the promise specification and it is done this way to guarantee that .then() handler are ALWAYS asynchronous and thus have a consistent and predictable behavior, regardless of whether the promise is resolved immediately or some time in the future. It makes writing and testing code a lot simpler.
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].
"platform code" here means that the current execution of Javascript finishes and then when the stack is completely empty of any Javascript, then and only then are promises .then() handlers called.
So, in my example above, the promise is resolved immediately, and the .then() handler is scheduled immediately, but it is scheduled to run AFTER the rest of the synchronous code finishes (essentially on the next turn of the event loop).
In the ES6 specification, when a promise resolves, its .then() handlers are scheduled with enqueJob(). The concept of jobs in ES6 is described here. What's relevant here is that they run AFTER the currently executing Javascript is done.
p.then happens in the future. While node waits for the then promise to resolve it will execute the next line of code.
Which is the console.log("End Of Program");
If you want to execute your Promises and async functions in a linear way, you must use the
ES2017 async/await syntax
Any Promise inside a async function can use the syntax await, for that you don't have to use .then() or .catch()
(async function () {
try {
var status = await p()
console.log(status)
console.log("End Of Program")
} catch (e) {
console.log(e) //If p reject, you catch it here
}
})()
And you should write your Promise like this
var p = new Promise(function(resolve,reject) {
/* When you use asynchronous functions, they all have a callback
This callback may have some error and data params, so,
you use them to resolve or reject your promise
*/
someAsyncFunction('blabla', function(error, data) {
if (error)
reject(error)
else
resolve(data)
})
})
I don't pretend for academic answer but I already asked amost the same question
today, about promises.
Actuall answer is:
When you ask to do work in JS, what is says that, "OK, this is going to take some time, but I promise that I'll let you know after the work is done. So please have faith on my promise, I'll let you know once the work is complete", and it immediately gives you a promise to you.
That's why you see your:
End Of Program
Promise Fullfilled
The actuall scheme looks like this:
│
├┐
│↓ promise
↓
console.log
I'm learning to convert my Node.js code style from callback to promise which seems to be the tendency and it has many advantages. To prevent the misunderstanding of the important points and benefits of the promise, I'm reading
document on MDN . I can understand Examples in this page, but I am not clear with the note that at the beginning of the document, it had mentioned :
Note: ... If the first argument is omitted or provided a non-function,
the new Promise that is created simply adopts the fulfillment state of
the Promise that then is called on (if it becomes fulfilled). If the
second argument is omitted or provided a non-function, the new Promise that
is created simply adopts the rejection state of the Promise that then is called
on (if it becomes rejected).
Sorry in advance if this is trivial.
Hope for the explanation with examples, thanks.
EDIT: Updated to better match the spec - see here and here.
The .then method calls one of two functions, depending on whether or not the Promise it's attached to has fulfilled or rejected. It then returns a new Promise based on the result of that call. This allows you to run different logic depending on whether or not a Promise was successful, and makes it possible to chain Promises together easily.
If you decide not to pass in one of those functions, however, it uses sensible defaults - which is what the note you posted is alluding to. It's probably a lot easier to demonstrate than to describe, though!
If you leave out the onRejected callback, like so:
myPromise.then(function (value) {
console.log("success!");
});
The result is the same as doing this:
myPromise.then(function (value) {
console.log("success!");
}, function (reason) {
throw reason;
});
If you leave out the onFulfilled callback, like so:
myPromise.then(null, function (reason) {
console.log("fail!");
});
The result is the same as:
myPromise.then(function (value) {
return value; // the same as returning Promise.resolve(value)
}, function (reason) {
console.log("fail!");
});
If you leave both out... it's basically pointless, but still:
myPromise.then();
This is effectively the same thing as:
myPromise.then(function (value) {
return value;
}, function (reason) {
throw reason;
});
Which is in turn, basically just:
myPromise
The other answers provide good explanations, but it might be easier to express this using the concept of skipping over.
If and when the promise fulfills, then the first argument to then is invoked (as long as it is a function); if and when the promise rejects, then the second argument to then is invoked (as long as it is there and is a function). In other cases, the .then is just skipped over.
Purists would object that it is incorrect to say the .then is "skipped over", and that what is really happening is that the .then creates a new promise which is equivalent to (or assumes the state of) the original promise. That's technically correct, but informally it's easier to talk and think about the .then getting "skipped".
Examples:
function log(v) { console.log("value", v); }
function err(e) { console.log("error", e); }
Promise.resolve(1).then(null, err).then(log);
^^^^^^^^^^^^^^^ "SKIPPED" (onFulfilled is null)
Promise.reject(1).then(log).catch(err);
^^^^^^^^^ "SKIPPED" (onRejected is not given)
For completeness, the corresponding behavior for catch is:
If and when the promise rejects, then the argument to catch is invoked (as long as it is a function). In other cases, the .catch is just skipped over.
Promise.resolve(1).catch(err).then(log);
^^^^^^^^^^ "SKIPPED" (promise did not reject)
If you think about it carefully, you will see that this all means that .then(null, handler) is exactly equivalent in every way to .catch(handler). So catch can be thought of as a kind of convenience routine.
But what's the point of allowing, but then ignoring, non-function handlers? Shouldn't they throw a TypeError or something instead? Actually, this is a "feature" which can be used as follows:
promise.then(isWednesday && wednesdayHandler)
If it's Wednesday, then this will evaluate to wednesdayHandler and we can do some special Wednesday processing. It it's not Wednesday, then this will evaluate to false, which is obviously not a function, so the whole then clause will be "skipped".
I don't have too much to add to this that the other answers don't already--just that I came to this question in the process of writing my own question about this, at which point SO miraculously suggested this one as a possible duplicate (when it did not come up in my previous search attempts).
Anyways the current documentation on MDN on the parameters to Promise.prototype.then have changed a bit since the OP and I think are still confusing, if not even moreso than before, so I thought I would add a follow-up answer concerning the current documentation which reads:
onFulfilled Optional
A Function called if the Promise is fulfilled. This function has one argument, the fulfillment value. If it is not a function, it is internally replaced with an "Identity" function (it returns the received argument).
What's confusing about this wording is it's not totally clear what is the antecedent of "received argument". Without already being familiar with the details of the Promises spec, one could read this as the "identity" function returning the argument received by the then() call. I.e. that something like this:
let promise = new Promise((resolve, reject) => resolve("value"));
let chained = promise.then("new value");
is equivalent to this:
let promise = new Promise((resolve, reject) => resolve("value"));
let chained = promise.then(() => "new_value");
so that the value resolved from chained is "new_value".
When in fact, "received value" refers to the resolved value of the original promise that .then() was chained from, i.e.:
let promise = new Promise((resolve, reject) => resolve("value"));
let chained = promise.then(() => "value");
So writing promise.then("not_a_callable") completely ignores "not a callable" and is resolved to the value that fulfilled the original promise, whatever it happens to have been.
Personally, I think this confusing behavior, but it is what it is.
According to MDN:
The executor function is executed immediately by the Promise implementation, passing resolve and reject functions
What were the actual reasons for this decision?
Why promises are not lazy?
What were the actual reasons for this decision?
The revealing Promise constructor with the callback was just an improvement over the older deferred pattern. The callback was never meant to provide choice over the evaluation time, it is supposed to provide a scope with error handling for the resolver functions.
Why promises are not lazy?
Because promises represent the asynchronous result values, nothing more. They're kept simple, without featuring lazyness (and representing the whole computation, with methods to start/repeat/etc). You can trivially get that by using a function that returns a promise instead.
Promises are meant to provide continuations, and actual state of standard Promise object proposes the following pattern:
function doStuff() {
return new Promise((resolve, reject) => { // execution function
// Do stuff here
});
}
I would ask a question myself to understand why Promise's execution function is called immediatelly: where you would do the promised stuff?
Clearly, it's called immediatelly because you're making a promise that some stuff will be successfully or unsucessfully done, and that stuff should start to be processed as soon as you call the enclosing function to avoid confusion to the caller.
The point of a promise is to have something to return from a function that callers can attach their callbacks to rather than pass them in. The constructor is a red herring to answer "why", as it exists solely to wrap old callback-style code in an imperfect world.
All JS functions are synchronous, even those returning a promise (es8's async is syntactic sugar).
The Promise constructor executor function exists to provide unified error handling. Consider:
function foo() {
""(); // throws TypeError
return Promise(resolve => {
""(); // rejects promise with TypeError
});
}
Callers would need both try{ foo().catch(failed); } catch(e) { failed(e); } = Sucks.
So put all your synchronous code inside the executor function to unify errors for your callers. That's what it's for, which I think is your real question.
"Lazy" execution would defeat the purpose of unifying error handling of all your code.
As I plunge into studying Promises, my understanding has halted on the following question that I do not find discussed (all I find are specific discussions of the Promise constructor, and the Promise 'then' function - but not a discussion that compares their design patterns).
1. The Promise constructor
From the MDN documentation, we have this use of the Promise constructor (with my comment added):
new Promise(function(resolve, reject) { ... }); // <-- Call this Stage 1
Function object with two arguments resolve and reject. The first
argument fulfills the promise, the second argument rejects it. We can
call these functions, once our operation is completed.
2. The then function
Moving on to the then function that can be called on a Promise object (which returns a new Promise object), we have the following function signature as described by the documentation (with my comments added):
p.then(onFulfilled, onRejected);
Chaining
Because the then method returns a Promise, you can easily chain then
calls.
var p2 = new Promise(function(resolve, reject) {
resolve(1); // <-- Stage 1 again
});
p2.then(function(value) {
console.log(value); // 1
return value + 1; // <-- Call this Stage 2
}).then(function(value) {
console.log(value); // 2
});
My question
From the above code snippet, it seems clear to me that the value passed to the resolve function in Stage 1 (in the second occurrence of resolve - beneath (2), above) is passed on to the next stage (the first then function that follows in the same code snippet). There is no return value at Stage 1. However, it is the return value at Stage 2 that is passed on to the next stage after that (the second then function).
Is this lack of correspondence between the design pattern for the creation of a Promise, and the use of the then function on an existing promise (which also returns a Promise), just a historical fluke (one requires calling a callback but returns nothing, and the other returns a value but does not call a callback)?
Or am I missing an underlying reason why the Promise constructor utilizes a different design pattern than the then function?
Bergi's answer is excellent, and has been very helpful to me. This answer is complementary to his. In order to visualize the relationship between the Promise() constructor and the then() method, I created this diagram. I hope it helps somebody... maybe even me, a few months months from now.
The main idea here is that the "executor" function passed to the Promise() constructor sets tasks in motion that will set the state of the promise; whereas the handlers you pass to then() will react to the state of the promise.
(Code examples adapted from Jake Archibald's classic tutorial.)
This is a highly simplified view of how things work, leaving out many important details. But I think if one can keep a grip on a good overview of the intended purpose, it will help avoid confusion when one gets into the details.
A couple of selected details
The executor is called immediately
One important detail is that the executor function passed to the Promise() constructor is called immediately (before the constructor returns the promise); whereas the handler functions passed to the then() method will not be called till later (if ever).
Bergi mentioned this, but I wanted to restate it without using the terms a/synchronously, which can be confused if you're not reading carefully: The distinction between a function calling something asynchronously vs. being called asynchronously is easy to gloss over in communication.
resolve() is not onFulfill()
One more detail I'd like to emphasize, because it confused me for a while, is that the resolve() and reject() callbacks passed to the Promise() constructor's executor function are not the callbacks later passed to the then() method. This seems obvious in retrospect, but the apparent connection had me spinning in circles for too long. There is definitely a connection, but it's a loose, dynamic one.
Instead, the resolve() and reject() callbacks are functions supplied by the "system", and are passed to the executor function by the Promise constructor when you create a promise. When the resolve() function is called, system code is executed that potentially changes the state of the promise and eventually leads to an onFulfilled() callback being called asynchronously. Don't think of calling resolve() as being a tight wrapper for calling onFulfill()!
There is no correspondence between the Promise constructor and the then method because they are two independent things, designed for different purposes.
The Promise constructor is only used for promisifying1 asynchronous functions. Indeed, as you say, it is built on invoking resolve/reject callbacks to asynchronously send values, and there are no return values in that case.
That the Promise constructor itself does take this "resolver" callback (to which it synchronously passes resolve and reject) is in fact an enhancement of the older deferred pattern, and bears no intended similarity to the then callbacks.
var p = new Promise(function(res, rej) { | var def = Promise.Deferred();
setTimeout(res, 100); | setTimeout(def.resolve, 100);
}); | var p = def.promise;
The then callbacks in contrast are classical asynchronous callbacks, with the additional feature that you can return from them. They are being invoked asynchronously to receive values.
p.then(function(val) { … });
To sum up the differences:
Promise is a constructor, while then is a method
Promise takes one callback, while then takes up to two
Promise invokes its callback synchronously, while then invokes its callbacks asynchronously
Promise always invokes its callback,
then might not invoke its callbacks (if the promise is not fulfilled/rejected)
Promise passes the capabilities to resolve/reject a promise to the callback,
then passes the result value / rejection reason of the promise it was called on
Promise invokes its callback for the purpose of executing side effects (call reject/resolve),
then invokes its callbacks for their result values (for chaining)
Yes, both do return promises, though they share that trait with many other functions (Promise.resolve, Promise.reject, fetch, …). In fact all of these are based on the same promise construction and resolve/reject capabilities that also the Promise constructor provides, though that's not their primary purpose. then basically offers the ability to attach onFulfilled/onRejected callbacks to an existing promise, which is rather diametral to the Promise constructor.
That both utilise callbacks is just coincidential - not a historical fluke, but rather coadaption of a language feature.
1: Ideally, you would never need this because all natively asynchronous APIs return promises
Inspired by the previous answers (I'll address the part that was most confusing to me):
The resolve and reject arguments in the Promise constructor are not functions you define. Think of them as hooks that you get to embed into your async operation code (usually you resolve with success response and reject with failure reason) , so that javascript has a way to eventually mark the Promise as Fulfilled or Rejected depending on the outcome of your async operation; once that happens, the appropriate function you defined in then(fun1, fun2) is triggered to consume the Promise (either fun1(success_response) or fun2(failure_reason), depending on whether the Promise is Fulfilled/Rejected). Since fun1 and fun2 are plain old javascript functions (they just happen to take the future outcome of your async operation as arguments), they return values (which can be undefined if you don't explicitly return).
Also see great articles by Mozilla:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
The whole point of the promise constructor executor function is to disseminate resolve and reject functions to non-promise-using code, to wrap it and convert it to use a promise. If you wanted to limit this to synchronous functions only, then yes, a return value from the function could have been used instead, but that would have been silly since the useful part is to disseminate the resolver and reject functions to code that actually runs later (way after the return), e.g. to callbacks passed in to some asynchronous API.
Here is the execution flow of Promise.
var p = new Promise((resolve, reject) =>{
console.log("1");
resolve("OK");
});
//The above code creates a promise and execustion starts immediately.
//it happens aynchronously. So the execution will not be blocked.
//Promise exustion will not wait for 'then' call on promise
console.log("2");
//The above line displays 2 on the console.
p.then((result)=>{
console.log("3");
console.log(result);
});
//The above code shoud not block execution. So it may print 4 first
// then 3
console.log("4");