I'm so much confused about what happens behind the scenes when promise is produced and consume. Please clarify my points and sorry for my weak English.
blank object is created with new keyword Promise constructor is
called and new keyword set the this of Promise constructor points to
the blank object this = blankobject.
Promise constructor receives callback (executor function) in argument
and calls the executor function.
executor function receives two callbacks (resolve,reject) as arguments
setTimeout gets called in the executor function and setTimeOut is
async code
async code goes to background and then Promise constructor returns
Promise object formerly blank object and Promise object reference
saved to myPromise.
a variable is created
What happens next ? When then method is called the code of then method goes to background? I imagine it goes to background and a variable is console.log // 10
After main code execution finishes, async code start setTimeout callback begins to execute and after execution finishes promise is fulfilled and resolved function returns value. How is this value stored in promise object and what happens in then method ?
let myPromise = new Promise (
(resolve, reject) => {
setTimeout(() => {
console.log(getIDs)
resolve(10);
}, 1500);
}
)
let a = 10
myPromise.then(val => {
console.log(val);
})
console.log(a)
The following is a simplified implementation of the built-in Promise class. catch and finally have not been implemented.
The function supplied to the Promise constructor is called the executor function, and is invoked immediately and synchronously.
Every promise has a method .then, enabling the chaining of promises.
Functions supplied to .then are always invoked asynchronously on a microtask (note use of queueMicrotask below).
Every time .then is called, a new promise is created and returned.
.then can be called more than once on the same promise, creating a multicast of the result of the promise, and a branching of the promise chain.
A promise can be in one of three states: pending, fulfilled, or rejected. State transitions are unidirectional: you cannot move from fulfilled or rejected, back to pending.
If a promise is resolved with another promise, then the two promise chains are joined and the outer promise takes on the status of the inner promise (which could be pending), until the inner promise resolves.
function Promise(executor) {
if (!executor) throw "Promise executor undefined"
let status = "pending", value, thenQ = []
const then = onFulfilled => {
let resolver
// This ensures control does not move to later promises
// until prior promises have been resolved.
const nextPromise = new Promise(resolve => (resolver = resolve))
// More than one "then" can be registered with each promise.
thenQ.push((...args) => resolver(onFulfilled(...args)))
return nextPromise
}
// We check if the result is a "thenable"; if so, we treat
// it as an inner promise, otherwise we simply fulfil with
// the result.
const resolve = result => result?.then ? result.then(fulfil) : fulfil(result)
// When a promise has been fulfilled, its "thens" can be run.
const fulfil = result => (status = "fulfilled", value = result, executeThens(value))
// "Thens" are run asynchronously, on a microtask.
const executeThens = value => queueMicrotask(() => thenQ.forEach(el => el(value)))
// The executor is run synchronously.
executor(resolve)
return {
then,
get status() { return status },
get value() { return value }
}
}
// Chaining
new Promise(resolve => {
console.log('Waiting for step 1...')
setTimeout(() => resolve("One, two..."), 1500)
})
.then(result => new Promise(resolve => {
console.log('Waiting for step 2...')
setTimeout(() => resolve(`${result}three, four`), 1500)
}))
.then(result => console.log(`Chaining result: ${result}.`))
// Branching
const p = new Promise(resolve => {
console.log('Waiting for step a...')
setTimeout(() => resolve("Alpha, Bravo..."), 1500)
})
p.then(result => new Promise(resolve => {
console.log('Waiting for step b1...')
setTimeout(() => resolve(`${result}Charlie, Delta`), 1500)
})).then(console.log)
p.then(result => {
console.log('Waiting for step b2...')
return `${result}Echo, Foxtrot`
}).then(console.log)
See also.
I'll go through your code in the order of execution.
At all times the value of this is whatever it was at the beginning. That's because you're only using arrow functions. But that's not relevant since you're not referencing this.
Main Code
let myPromise = new Promise(executor); creates a pending promise object. While creating the promise, the executor function will be executed.
setTimeout(callback, 1500); puts the callback function on some internal timer queue. The javascript engine promises to do its best to execute callback after (at least) 1500ms.
let a = 10; sets the variable a to 10.
myPromise.then(onFulfilled); creates another pending promise. It is linked to myPromise so that onFulfilled will be scheduled asynchronously when myPromise is fulfilled.
console.log(a); prints the value of a which is 10.
For the next 1500ms nothing happens. Then callback gets executed.
callback of setTimeout
console.log(getIDs); prints getIDs. From the name you can guess it's a function. So something like [Function: getIDs] will be printed.
resolve(10); fulfills myPromise and sets its result to 10. Since myPromised is now fulfilled, onFulfilled of anotherPromise gets scheduled asynchronously.
Now we have to wait for the call stack to process. After that, onFulfilled will be called.
onFulfilled of myPromise.then
console.log(val); prints the content of val. That is, the result of myPromise.
Related
From MDN:
Promise.all is rejected if any of the elements are rejected. For example, if you pass in four promises that resolve after a timeout and one promise that rejects immediately, then Promise.all will reject immediately.
Let's discuss the following code snippet:
(async () => {
try {
const awaited = await Promise.all([
(() => {
console.log('1');
return Promise.reject('2');
})(),
(() => {
console.log('3');
return Promise.resolve('4');
})(),
]);
console.log(awaited);
} catch (error) {
console.log(error);
} finally {
console.log('5');
}
})();
All the promises in the code above are immediately resolved/rejected. I think that the code should execute this way:
First console.log is executed, thus logging '1';
Promise is rejected with the value '2' thus fast-failing Promise.all();
'Catch' clause is executed, logging the rejected value of '2';
'Finally' clause is executed regardless, logging '5';
Instead, I see a '3' also being logged. Why is it so?
Instead, I see a '3' also being logged. Why is it so?
All the other answers try to explain how promises or the event loop work, but in fact it has nothing to do with those.
The argument you pass to Promise.all is an array that contains the results of two function calls. Therefore both functions will be called before Promise.all is called.
Both functions console.log a value and then return a promise.
Promise.all receives an array containing those promises. So before Promise.all does anything, both functions have already been executed and logged 1 and 3 respectively.
Maybe it's easier to see you restructure your code to create the promises beforehand:
(async () => {
try {
const promise1 = (() => {
console.log('1');
return Promise.reject('2');
})();
const promise2 = (() => {
console.log('3');
return Promise.resolve('4');
})()
const awaited = await Promise.all([promise1, promise2]);
console.log(awaited);
} catch (error) {
console.log(error);
} finally {
console.log('5');
}
})();
Just like with any other function, all function arguments are evaluated before the function itself is called. The order of events is rather this:
First console.log is executed, thus logging '1';
Rejected promise with 2 is created.
Second console.log is executed, thus logging '3';
Fulfilled promise with 4 is created.
Array containing both promises is created.
Promise.all is called with array of promises as parameter.
Promise.all processes promises.
'Catch' clause is executed, logging the rejected value of '2';
'Finally' clause is executed regardless, logging '5';
Here is a simpler example: Note that nothing is "done" with the second argument passed to the function and yet the value is still logged, precisely because all arguments are evaluated first:
function process(arg) {
return 'Received ' + arg;
}
const result = process(
(() => {
console.log('1');
return 1;
})(),
(() => {
console.log('3');
return 2;
})(),
);
console.log(result)
This is due to the way that Promises are handled in JavaScript. It is asynchronous in nature and there is no security as to the execution order. Please note that this is not an error but rather a feature of the language.
I would suggest you read more on the issue in this SO question and about the event loop.
The Promise.all() does fast-fail. However, it doesn't abort the other Promises from finishes. This example with timeouts illustrates it a little better.
Promise.all([
Promise.reject(),
new Promise((resolve, reject) => setTimeout(() => { console.log('a'); resolve(); }, 500)),
new Promise((resolve, reject) => setTimeout(() => { console.log('b'); resolve(); }, 2000))
])
.then(() => console.log('done'))
.catch(() => console.log('fail'));
You can see that the Promise.all() calls the catch() almost immediately, and stops waiting for the results of the other, because it won't change the outcome. However, the other two Promises aren't aborted in any way.
If you want to abort them, you'd have to implement your own logic to do that.
Please consider this code:
function testPromise() {
resolver = function(resolve, reject) {
resolve('foo');
console.log('#resolver');
}
setTimeout(() => console.log('Timeout'), 0);
console.log('Before');
const p = new Promise(resolver);
console.log(p);
p.then((value) => console.log('#then1:', value));
p.then((value) => console.log('#then2:', value));
console.log('After');
}
Running this function gives the following output in Firefox console:
Before
#resolver
Promise { <state>: "fulfilled", <value>: "foo" }
After
#then1: foo
#then2: foo
Timeout
Although the promise executor doesn't do anything asynchronous and calls the resolve callback parameter right away, and the console output confirms that the promise state is fulfilled, nevertheless the callback function that is passed to promise's then() method is called later, after execution of the currently running code is finished. I could get the same results if the resolve() call would be put in setTimeout: setTimeout(() => resolve('foo'), 0);
I'd like to understand more about this behavior: when exactly this then callback is called? Immediately after the currently executed code exits? Or there could be some other queued-up code that gets executed before? From the test code above, it can be observed that the zero-time timeout callback is executed later.
This is my function
const f = (value,err) => {
if(!value === Number){
throw err;
}
return (value);
}
Using promises I want to return an object of parameter value and after some wait it will return parameter in number. Is that possible?
A promise doesn't do anything in and of itself, and it doesn't make anything asynchronous in and of itself (other than that promise reactions are always asynchronous¹). A promise is just a standardized way to report the completion of something that's (potentially) asynchronous. So the literal answer to your question is: No, you can't use a promise to do that, not on its own. You'd have to combine it with something like setTimeout or similar.
Also note that if (!value === Number) is always false. It's evaluated like this: !value, negating the value of value, and then x === Number, which will always be false because there is no value that, when negated, turns into the Number function.
But for instance, if you wanted to check whether something is a number but not respond for 100ms:
const f = (value, err) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (typeof value !== "number") {
reject(err);
} else {
resolve(value);
}
}, 100);
});
};
Other than the promise, the two significant changes there are:
Using setTimeout to introduce asynchronousness, and
Changing the if condition to something that won't always be false (I may or may not have guessed correctly what you wanted there :-) )
¹ A promise reaction is a call to a then, catch, or finally callback registered on the promise. Here's an example of what I mean by "promise reactions are asynchronous:"
console.log("Before creating the promise");
new Promise(resolve => {
console.log("Inside the promise executor function");
resolve(42);
})
.then(value => {
console.log(`Promise reaction ran, value = ${value}`);
});
console.log("After creating the promise");
That code has this output:
Before creating the promise
Inside the promise executor function
After creating the promise
Promise reaction ran, value = 42
Notice that everything was synchronous except the call to the then callback (the promise reaction), which per specification is always done asynchronously, even if (as in this case) the promise is already settled when the reaction is added to it.
Node.JS 10.15, serverless, lambdas, invoked locally
SAMPLE A) This Works:
export async function main(event) {
const marketName = marketIdToNameMap[event.marketId];
const marketObject = marketDirectory[marketName];
const marketClient = await marketObject.fetchClient();
const marketTime = await marketObject.getTime(marketClient);
console.log(marketTime);
}
SAMPLE B) and this works:
export function main(event) {
const marketName = marketIdToNameMap[event.marketId];
const marketObject = marketDirectory[marketName];
marketObject.fetchClient().then((marketClient)=>{
marketObject.getTime(marketClient).then((result) => {
console.log('<---------------> marker 1 <--------------->');
console.log(result);
});
});
}
SAMPLE C) but this does not:
export async function main(event) {
const marketName = marketIdToNameMap[event.marketId];
const marketObject = marketDirectory[marketName];
const marketClient = await marketObject.fetchClient();
console.log('<---------------> marker 1 <--------------->');
marketObject.getTime(marketClient).then((result) => {
console.log('<---------------> marker 22 <--------------->');
console.log(result);
});
}
the guts of getTime are for all examples:
function getTime(marketClient){
return new Promise((resolve, reject) => {
return marketClient.getTime((err, result) => {
if (err) {
reject(err);
}
resolve(result);
});
}).catch(err => {
throw err;
});
}
clearly, it seems to be an issue with mixing async/awaits with classic promise then-ables. I would expect SAMPLE C to work because getTime() is returning a promise. However, the code simply finishes silently, never hitting the second marker. I have to put the first marker there just to be sure any code is run at all. It feels like i should be able to mix async/await and thenables, but i must not be considering something here.
#adrian, nope
You're neither awaiting nor returning the promise from marketObject.getTime().then(), and this will result in that promise chain executing independently, the main function returning and the process closing. Remember.. then returns a promise too.
the solution is
await marketObject.getTime(marketClient).then(...
or
return marketObject.getTime(marketClient).then(...
either way chains the promise to the main function such that whatever executes it consistently waits for all promises to resolve (or reject).
I suspect Sample B works because the main is not async and Lambda will wait for the event-loop to complete. i.e. it will execute the promise chain even though main returned early.
https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html
If you don't use callback in your code, AWS Lambda will call it
implicitly and the return value is null. When the callback is called,
AWS Lambda continues the Lambda function invocation until the event
loop is empty.
... and I suspect if you return a Promise (as you do in Sample C) then Lambda will simply terminate the process immediately once it resolves, which is does because you don't await/return the .then() chain, and thus the floating promise chain you've created will not execute.
Let's say I have a function like this
const fn = () => {
return work()
.then((finalResult) => { // then 1
finish(finalResult);
});
};
const work = () => {
return Promise.resolve(1)
.then(() => { // then 2
return process1();
}).then((result) => { // then 3
return process2(result);
});
};
My question is: can I rely on the fact that finish will be called AFTER process1 and process2. Specifically, is then 1 attached to the promise returned by Promise.resolve(1) or is it attached to the promise returned by then 3.
This is foremost a matter of syntax. What you are doing is equivalent to
const fn = () => {
const promiseA = work();
const promiseB = promiseA.then(finish); // then 1
return promiseB;
};
const work = () => {
const promiseC = Promise.resolve(1);
const promiseD = promiseC.then(process1); // then 2
const promiseE = promiseD.then(process2); // then 3
return promiseE;
};
just introducing some extra variables (and also I've simplified the function expressions).
Now we can clearly see that the result of work() is what is returned by it, so promiseA === promiseE. So yes, finish is chained to the result of the "then3" call.
Knowing that then creates a new promise (for the eventual result of the callback) instead of returning the original one, we can also conclude that promiseE !== promiseC, so finish is not chained onto Promise.resolve(1).
Each then will await its preceding promise and then run in turn.
If the preceding promise returns a value, the argument passed to the callback will be that value, or if it returns another promise, the argument to the callback will be the value returned from that promise.
Section 2.2.6.1 of the promise plus specification states:
If/when promise is fulfilled, all respective onFulfilled callbacks must execute in the order of their originating calls to then.
It is guaranteed that finish(finalResult) will run with the result of process2(result) (unless there's an error, of course.)
I found this article to be extremely enlightening on getting to grips with the guts of promises.