Javascript Promises and optional arguments [duplicate] - javascript

This question already has answers here:
Where do the promise callback arguments come from
(3 answers)
When or who does pass resolve and reject functions to JS promises?
(6 answers)
Closed 2 months ago.
I'm trying to understand Promises and their optional arguments.
(I'm assuming that because the arguments are optional, that too few are acceptable, and that too many arguments are also acceptable).
As an example:
let myPromise = new Promise(function(first, second, third) {
let x = 0;
if (x == 0) { //<<true
second();
} else {
third();
}
});
myPromise.then(
function() {
console.log("1");
},
function() {
console.log("2");
}
);
Runs the second function and outputs "2".
let myPromise = new Promise(function(first, second, third) {
let x = 0;
if (x != 0) { //<<false
second();
} else {
third();
}
});
myPromise.then(
function() {
console.log("1");
},
function() {
console.log("2");
}
);
Also runs the second function and outputs "2".
In the first case, is the Promise calling the function by name; And in the second case, calling the function by position?
How exactly does a Promise know which function to call?

The order of arguments matters because the order values are passed in determines which variables they are assigned to.
The callback to new Promise is given two arguments. A function to call to resolve the promise and a function to call to reject it. It doesn't get passed a third argument so third is undefined.
The then method takes two arguments. A function to call if the promise is resolved and a function to call if the promise is rejected.
In your second example, the promise is rejected. This isn't because you called the reject function, but because you attempted to call undefined as if it was a function causing an exception to be thrown.
const promise = new Promise((resolve, reject, third) => {
console.log(typeof resolve);
console.log(typeof reject);
console.log(typeof third);
third();
});
promise.then(
(value) => console.log("Resolved", value),
(error) => console.log("Rejected", error.message)
);

The first argument to the constructor callback is the resolver function - call it, and it'll result in the returned Promise being fulfilled.
The second argument is a function that, if called, will result in the Promise rejecting.
The third (and all subsequent) arguments are undefined. You're free to define them when constructing a Promise, but their values will always be undefined.
In the first snippet, the second argument is called, rejecting the Promise.
In the second snippet, you call the third argument - but the third argument is not a function. A synchronous error thrown inside the Promise constructor results in the Promise rejecting. Check the error.message in the error handler for better understanding:
let myPromise = new Promise(function(first, second, third) {
third();
});
myPromise.then(
function() {
console.log("1");
},
function(x) {
console.log(x.message);
}
);
So it's not that you're free to call whichever positional argument you wish, and the engine will magically understand what you were trying to do - it's that anything that results in an error being thrown will result in the Promise getting rejected, no matter the arguments.

Related

Should I always return a promise when I have an async function?

When I write an async function it usually returns a promise:
export const myPromiseFunction = async (params) => {
// some logic
return Promise.resolve('resolved-value');
});
But I was wondering if it would be a mistake if this function would not return a promise, so for example:
export const myPromiseFunction = async (params) => {
// some logic
params.map(async (param) => {
await printParam(param);
async function printParam(par) {
// do some other stuff
Printer.print(par);
});
});
});
export class Printer {
public static async print(par) {console.log(par);} // I know it could not be async, but for the sake lets suppose it does
}
Is this a mistake / bad practice ? Or can we find a scenario when this will be valid and desirable ?
All async functions automatically return Promises. If you declare a function as async, it will return a Promise, even if your only return value is a simple value like a string or number. If you don't return explicitly, your async function will still return a Promise with a value of undefined.
In fact, it is more common for the body of an async function to return a simple value rather than a promise; the assumption is that your function is async because you await the promises you consume within it. As a consequence, even if you return the value 5, the return value is a Promise (that resolves to 5) representing the potential delay that comes from any await expressions in the function.
You don't have to return a Promise object explicitly in your async function, and it is redundant to do so if you're just wrapping a simple value like 'resolved-value'. Conversely, you can make a normal function behave like an async function if you always return a Promise (potentially with Promise.resolve) and you never synchronously throw an error within it.
async function myFunction() {
makeSomeCall(); // Happens immediately.
await someOtherPromise(); // myFunction returns a Promise
// before someOtherPromise resolves; if it
// does without error, the returned Promise
return 5; // resolves to 5.
}
/** Behaves the same as the above function. */
function myFunctionButNotAsync() {
try {
makeSomeCall();
// If you didn't have someOtherPromise() to wait for here, then
// this is where Promise.resolve(5) would be useful to return.
return someOtherPromise().then(() => 5);
} catch (e) {
return Promise.reject(e);
}
}
All that said, you may have occasion to explicitly return a Promise object (such as one produced by Promise.all or a separate Promise-returning function), which then observes rules similar to what Promise.resolve() observes: If the object you return from an async function is a Promise, or has a then function, then the automatic Promise the async function returns will wait for the specific Promise or Promise-like object you pass back with return.
async function myFunction() {
makeSomeCall(); // Happens immediately.
await anythingElse(); // You can still await other things.
return someOtherPromise(); // The promise myFunction returns will take
// the same outcome as the Promise that
// someOtherPromise() returns.
}
In a related sense, this is why return await is seen as redundant, though as described it does make a difference for the stack traces that you see if the wrapped promise is rejected.
Short answer: no, an async function doesn't have to returns a Promise. Actually, generally you wouldn't return a Promise object (unless you're chaining asynchronous events).
What async and await do is wait for a response from something that returns a Promise.
You first code example actually returns a resolved Promise. But what happens if the Promise isn't resolved properly ?
It's best to call a function that returns a Promise from another async function:
function getRequestResult() {
return new Promise(resolve => {
setTimeout(() => {
resolve('request sent');
}, 2000);
});
}
async function sendMyRequest() {
console.log('Sending request');
const result = await getRequestResult();
console.log(result);
// expected output: "resolved"
}
You can send rejections/errors within getRequestResult() that way, and also manage how these errors will be managed by the call in sendMyRequest() (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await).

Returning values from Javascript Promise chain

Modern JS/Promise newbie here. I'm having trouble finding the answer to a simple question, despite the overwhelming amount of content on this subject.
I believe I have a decent understanding of JS Promises (thanks to various sources, including mdn and https://spin.atomicobject.com/2016/02/16/how-javascript-promises-work/)
I have consumed and produced Promises regularly in their simplest form, however I keep stumbling on methods in my inherited projects with the following pattern:
const doAsyncOp = () =>
$http.get(uri)
.then(
value => value,
reject => //...
);
My big question is: What happens when you simply return a value from your success handler? I need to consume this method, and need access to 'value' in the client code. Is this technically unhandled? Should I rewrite the implementation?
My big question is: What happens when you simply return a value from your success handler?
When you return a value from a .then() handler, that value becomes the resolved value of the parent promise chain: So, in this code:
// utility function that returns a promise that resolves after a delay
function delay(t, v) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, v), t);
});
}
function test() {
return delay(100, 5).then(val => {
// this return value becomes the resolved value of the promise chain
// that is returned from this function
return 10;
});
}
test().then(result => {
// logs 10
console.log(result);
});
When you run it, it will log 10 because of the return 10 in the .then() handler.
There are four possibilities from a .then() handler:
Return a regular value such as return 10 or return val. That value becomes the resolved value of the promise chain. If no value is returned (which in Javascript means the return value is undefined), then the resolved value of the promise chain is undefined.
Return a promise that ultimately resolves or is already resolved. This promise is added to the chain and the promise chain takes on the resolved value of that promise.
Return a promise that ultimately rejects or is already rejected. This promise is added to the chain and the promise chain takes on the rejected reason of the returned promise.
Throw an exception. If an exception is thrown inside the .then() handler, then the .then() infrastructure will catch it and turn the promise chain into a rejected state with the reject reason set to the value that is thrown. So, if you do throw new Error("User not found") inside a .then() handler, then that promise chain will be rejected with that error object as the reject reason.
In your specific code:
const doAsyncOp = () =>
$http.get(uri)
.then(
value => value,
reject => //...
);
There is no reason for the value => value. value is already the resolved value of the promise, you do not need to set it again.
And since the fat arrow function automatically returns any single statement, you're already returning the promise from $http.get().then() in your doAsyncOp() function. So, you can just do:
const doAsyncOp = () => $http.get(uri);
doAsyncOp().then(result => {
// handle the result here
}).catch(err => {
// handle error here
});
To get this to the client code, just return the promise from your function:
const doAsyncOp = () => $http.get(uri)
then in your client you can use:
doAsyncOp()
.then(value => {
// use value
}
.catch(err => { /* error */ }
I think you can use async and await for the resolved values.
function delay(t, v) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, v), t);
});
}
async function(){
await myData = delay(1000, 1000);
console.log(myData);
}

How can I bubble errors on Promises without calling `catch` at every level? [duplicate]

This question already has answers here:
What is the explicit promise construction antipattern and how do I avoid it?
(3 answers)
Closed 6 years ago.
I'm struggling to figure out how to properly bubble errors with promises.
Specifically, what I'm talking about is a situation where there is nested promises. I want to be able to implictly pass the resolve and reject functions to the nested promise.
It'll be more obvious what I mean by showing some code.
The following is what I have been using successfully:
var asyncIsEven = function(number) {
return new Promise(function(resolve, reject){
if (number % 2 == 0) { return resolve(number) }
else { return reject("number is odd") }
})
}
var A = function (number) {
return new Promise(function(resolve, reject){
return asyncIsEven(number).then(resolve).catch(reject)
})
}
Here, it seems pretty unnecessary to write then(resolve).catch(reject).
I understand that I can just make function A return asyncIsEven(number), but there are situations when I want to provide a then function but have no need to provide a catch function (or vice versa).
What I've tried:
this works, but is not really what I want because I'm not providing a then handler to the nested callback
var A = function (number) {
return Promise.all([asyncIsEven(number)])
}
// or alternatively
var A = function(number) {
return asyncIsEven(number)
}
This is what I want to write, but it doesn't work
var A = function(number) {
return new Promise(function(resolve, reject) {
return asyncIsEven(number).then(resolve)
})
}
When I say it "doesnt work", what I mean is that I cannot write the following:
A(2).then(function(number) {
console.log(`${number} is even`)
}).catch(function(err) {
console.log(err)
})
Because the catch function will never be called.
In other words - if I define function A to return asyncIsEven(number).then(resolve), how can I bubble errors from asyncIsEven up to the reject callback given to A?
In response to comments and the duplicate question, I can write the following answer:
Wrapping A's return value (or that of any function which returns a different Promise) with new Promise() or Promise.all is unnecessary.
Instead, just call return otherPromise().then(handler) and the reject function on otherPromise can be used with A().catch(handler).
When new Promise() is used to wrap this return value, the reject function on the inner promise will not bubble to A().catch(). However, using Promise.all to wrap the return value will bubble up the error.

Promise callbacks returning promises

With regard to these great two sources: NZakas - Returning Promises in Promise Chains and MDN Promises, I would like to ask the following:
Each time that we return a value from a promise fulfillment handler, how is that value passed to the new promise returned from that same handler?
For instance,
let p1 = new Promise(function(resolve, reject) {
resolve(42);
});
let p2 = new Promise(function(resolve, reject) {
resolve(43);
});
let p3 = p1.then(function(value) {
// first fulfillment handler
console.log(value); // 42
return p2;
});
p3.then(function(value) {
// second fulfillment handler
console.log(value); // 43
});
In this example, p2 is a promise. p3 is also a promise originating from p1's fulfillment handler. However p2 !== p3. Instead p2 somehow magically resolves to 43 (how?) and that value is then passed to p3's fulfillment handler. Even the sentence here is confusing.
Could you please explain to me what exactly is going on here? I am totally confused over this concept.
Let’s say that throwing inside then() callback rejects the result promise with a failure, and returning from then() callback fulfills the result promise with a success value.
let p2 = p1.then(() => {
throw new Error('lol')
})
// p2 was rejected with Error('lol')
let p3 = p1.then(() => {
return 42
})
// p3 was fulfilled with 42
But sometimes, even inside the continuation, we don’t know whether we have succeeded or not. We need more time.
return checkCache().then(cachedValue => {
if (cachedValue) {
return cachedValue
}
// I want to do some async work here
})
However, if I do async work there, it would be too late to return or throw, wouldn’t it?
return checkCache().then(cachedValue => {
if (cachedValue) {
return cachedValue
}
fetchData().then(fetchedValue => {
// Doesn’t make sense: it’s too late to return from outer function by now.
// What do we do?
// return fetchedValue
})
})
This is why Promises wouldn’t be useful if you couldn’t resolve to another Promise.
It doesn’t mean that in your example p2 would become p3. They are separate Promise objects. However, by returning p2 from then() that produces p3 you are saying “I want p3 to resolve to whatever p2 resolves, whether it succeeds or fails”.
As for how this happens, it’s implementation-specific. Internally you can think of then() as creating a new Promise. The implementation will be able to fulfill or reject it whenever it likes. Normally, it will automatically fulfill or reject it when you return:
// Warning: this is just an illustration
// and not a real implementation code.
// For example, it completely ignores
// the second then() argument for clarity,
// and completely ignores the Promises/A+
// requirement that continuations are
// run asynchronously.
then(callback) {
// Save these so we can manipulate
// the returned Promise when we are ready
let resolve, reject
// Imagine this._onFulfilled is an internal
// queue of code to run after current Promise resolves.
this._onFulfilled.push(() => {
let result, error, succeeded
try {
// Call your callback!
result = callback(this._result)
succeeded = true
} catch (err) {
error = err
succeeded = false
}
if (succeeded) {
// If your callback returned a value,
// fulfill the returned Promise to it
resolve(result)
} else {
// If your callback threw an error,
// reject the returned Promise with it
reject(error)
}
})
// then() returns a Promise
return new Promise((_resolve, _reject) => {
resolve = _resolve
reject = _reject
})
}
Again, this is very much pseudo-code but shows the idea behind how then() might be implemented in Promise implementations.
If we want to add support for resolving to a Promise, we just need to modify the code to have a special branch if the callback you pass to then() returned a Promise:
if (succeeded) {
// If your callback returned a value,
// resolve the returned Promise to it...
if (typeof result.then === 'function') {
// ...unless it is a Promise itself,
// in which case we just pass our internal
// resolve and reject to then() of that Promise
result.then(resolve, reject)
} else {
resolve(result)
}
} else {
// If your callback threw an error,
// reject the returned Promise with it
reject(error)
}
})
Let me clarify again that this is not an actual Promise implementation and has big holes and incompatibilities. However it should give you an intuitive idea of how Promise libraries implement resolving to a Promise. After you are comfortable with the idea, I would recommend you to take a look at how actual Promise implementations handle this.
Basically p3 is return-ing an another promise : p2. Which means the result of p2 will be passed as a parameter to the next then callback, in this case it resolves to 43.
Whenever you are using the keyword return you are passing the result as a parameter to next then's callback.
let p3 = p1.then(function(value) {
// first fulfillment handler
console.log(value); // 42
return p2;
});
Your code :
p3.then(function(value) {
// second fulfillment handler
console.log(value); // 43
});
Is equal to:
p1.then(function(resultOfP1) {
// resultOfP1 === 42
return p2; // // Returning a promise ( that might resolve to 43 or fail )
})
.then(function(resultOfP2) {
console.log(resultOfP2) // '43'
});
Btw, I've noticed that you are using ES6 syntax, you can have a lighter syntax by using fat arrow syntax :
p1.then(resultOfP1 => p2) // the `return` is implied since it's a one-liner
.then(resultOfP2 => console.log(resultOfP2));
In this example, p2 is a promise. p3 is also a promise originating from p1's fulfillment handler. However p2 !== p3. Instead p2 somehow magically resolves to 43 (how?) and that value is then passed to p3's fulfillment handler. Even the sentence here is confusing.
a simplified version how this works (only pseudocode)
function resolve(value){
if(isPromise(value)){
value.then(resolve, reject);
}else{
//dispatch the value to the listener
}
}
the whole thing is quite more complicated since you have to take care, wether the promise has already been resolved and a few more things.
I'll try to answer the question "why then callbacks can return Promises themselves" more canonical. To take a different angle, I compare Promises with a less complex and confusing container type - Arrays.
A Promise is a container for a future value.
An Array is a container for an arbitrary number of values.
We can't apply normal functions to container types:
const sqr = x => x * x;
const xs = [1,2,3];
const p = Promise.resolve(3);
sqr(xs); // fails
sqr(p); // fails
We need a mechanism to lift them into the context of a specific container:
xs.map(sqr); // [1,4,9]
p.then(sqr); // Promise {[[PromiseValue]]: 9}
But what happens when the provided function itself returns a container of the same type?
const sqra = x => [x * x];
const sqrp = x => Promise.resolve(x * x);
const xs = [1,2,3];
const p = Promise.resolve(3);
xs.map(sqra); // [[1],[4],[9]]
p.then(sqrp); // Promise {[[PromiseValue]]: 9}
sqra acts like expected. It just returns a nested container with the correct values. This is obviously not very useful though.
But how can the result of sqrp be interpreted? If we follow our own logic, it had to be something like Promise {[[PromiseValue]]: Promise {[[PromiseValue]]: 9}} - but it is not. So what magic is going on here?
To reconstruct the mechanism we merely need to adapt our map method a bit:
const flatten = f => x => f(x)[0];
const sqra = x => [x * x];
const sqrp = x => Promise.resolve(x * x);
const xs = [1,2,3];
xs.map(flatten(sqra))
flatten just takes a function and a value, applies the function to the value and unwraps the result, thus it reduces a nested array structure by one level.
Simply put, then in the context of Promises is equivalent to map combined with flatten in the context of Arrays. This behavior is extremely important. We can apply not only normal functions to a Promise but also functions that itself return a Promise.
In fact this is the domain of functional programming. A Promise is a specific implementation of a monad, then is bind/chain and a function that returns a Promise is a monadic function. When you understand the Promise API you basically understand all monads.

How to return a promise in a function from the last promise in a chain of "then"

I'm writing a test using Selenium and JavaScript. I'm new to both, and also new to functional programming and promises. I'm trying to create a function that needs to do 3 things:
Click on an input
Clear the input
SendKeys to input
My current function does not work:
var clearAndSendKeys = function(driver, elementIdentifier, sendKeys) {
var returnValue;
driver.findElement(elementIdentifier).then(function(inputField){
inputField.click().then(function() {
inputField.clear().then(function() {
returnValue = inputField.sendKeys(sendKeys);
});
});
});
return returnValue;
}
The function would then be called as for example:
clearAndSendKeys(driver, webdriver.By.id('date_field'), '14.09.2015').then(function(){
//Do stuff
});
I expected the variable returnValue to contain the promise from sendKeys. However the function clearAndSendKeys returns the undefined variable before sendKeys is ran. I assume this is because returnValue was never defined as a promise, and so the program does not know that it needs to wait for sendKeys.
How can I make my function clearAndSendKeys return the promise from sendKeys? I'd rather avoid having to add a callback to the clearAndSendKeys function.
Edit: Removed .then({return data}) from the code as this was a typo.
You have to return each promise from the .then callback:
var clearAndSendKeys = function(driver, elementIdentifier, sendKeys) {
return driver.findElement(elementIdentifier).then(function(inputField){
return inputField.click().then(function() {
return inputField.clear().then(function() {
return inputField.sendKeys(sendKeys);
});
});
});
}
The promise returned by .then will resolve to the same value as the value returned from the callback.
See Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference for why your current code does not work. Promises are asynchronous.
First of all its probably not the best idea to nest promises, completely defeating their main purpose of eliminating callback hell. then callback can return Thenable object that allows to create nice chains of async operations.
In this case you just need to store reference to input field available as a result of the first async operation in the scope of the main function and then create chain of async operations that can be returned from this function.
var clearAndSendKeys = function(driver, elementIdentifier, sendKeys) {
var inputFieldRef;
return driver.findElement(elementIdentifier)
.then(function(inputField){
inputFieldRef = inputField;
return inputField.click();
}).then(function() {
return inputFieldRef.clear();
}).then(function() {
return inputFieldRef.sendKeys(sendKeys);
});
}

Categories