I would like to chain promise object, but the problem that the second then block is executed before the chained promise object got resolved.
service.webService1()
.then(data => {
if (data.flag) { // true
return service.webService2() // returns a promise
}
return $q.resolve({})
})
.then(data => {
// some logic
// data is undefined here, should be the result of
// service.webService2() call
})
.catch(err => {
// err handling
})
The problem that I forgot to return my promise in the service.webService2() function. Thanks :)
Related
I wanted to know why the promise below both call the catch() and then() method.
function testpromise(){
return new Promise((resolve, reject)=>{
reject("Error");
})
}
testpromise()
.catch((err)=>{console.log(err)})
.then(()=>{console.log("Then")})
But why this one doesn't ? (I only moved the .then() method before the .catch() one).
function testpromise(){
return new Promise((resolve, reject)=>{
reject("Error");
})
}
testpromise()
.then(()=>{console.log("Then")})
.catch((err)=>{console.log(err)})
Is this a bug ? It's kind weird that I can't get the catch() method before the .then(). For me putting catch before then allow me to quickly check if I handle possible errors correctly. I have dozens of promises wrote like so and I just noticed that it will also call my .then() method right away, which is not good.
A catch returns a promise so unless you throw or return another rejected promise it will resolve to the next then() in the chain
Following contrived example should help you visualize it
testpromise()
.catch((err) => {
console.log('First catch=', err)
return 'Catch message'; // implicit return value, with no return next then receives undefined
})
.then((fromCatch) => {
console.log("from first catch =", fromCatch)
})
.then(testpromise) // return another rejected promise to next catch
.then(() => console.log('Not called'))
.catch((err) => {
console.log('Second catch');
throw 'New Error'
})
.then(() => console.log('Not called #2'))
.then(() => console.log('Not called #3'))
.catch(err => console.log('Final catch =', err))
function testpromise() {
return new Promise((resolve, reject) => {
reject("Error");
})
}
Consider the following code:
try {
throw new Error("catch me if you can!");
} catch (err) {
// whatever
}
console.log("hi");
Because that is the synchronous equivalent of this:
Promise.reject(new Error("catch me if you can!"))
.catch(_ => { /* whatever */ })
.then(_ => console.log("hi"));
Each time you add a .then or .catch it returns a new promise, it doesn't modify the old one in place. So it's not that the order is wrong, it's that it matters. Otherwise you couldn't do something like this:
fetch(someURL)
.then(response => response.json())
.then(doSomethingWithJSON)
.catch(err => {
handleRequestFailure(err);
console.error(err);
return err;
})
.then(result => {
if (result instanceof Error) {
return fallbackRequest();
}
})
.catch(handleFallbackError);
A .catch() is really just a .then() without a slot for a callback function for the case when the promise is resolved.
MDN Promises Documentation
Think of the .then() function as a chain, each one does something according to the resolved value, and passes the returned value to the next .then() in the chain, or if an error thrown, to the .catch() method.
I have a function which makes an XMLHttpRequest and returns a promise with the response from the request.
However I'd like to instead return a promise which contains just one string from the response.
E.g instead of the promise resolving to response = {status, data} etc, I'd like to return just response.data.some_field
How can I do this?
If you call .then on a promise, you'll produce another promise which will resolve to whatever you return in the callback function. So take the current promise that you're creating, and then add on the following:
.then((response) => {
return response.data.some_field;
});
So maybe the full function will look like this:
function getStuff() {
return new Promise((resolve, reject) => {
//somethingWithXMLHttpRequest
}).then((response) => {
return response.data.some_field;
});
}
What you're looking for is promise chaining. Link goes to mozilla's doc page on promise chaining.
function httpRequestAsync () {
// Return a promise... This is where your XMLHttpRequest takes place
}
function getStuffAsync() {
// Make your http request, chain the return promise,
// and return a promise, which resolves to the chosen field.
return httpRequestAsync() //Calling .then() on this promise is promise chaining.
.then((response) => {
return response.data.some_field;
});
}
function someBusinessLogicFunction () {
let yourString = "";
getStuffAsync()
.then((response) => {
yourString = response; // yourString does in fact equal the response param... :).catch(() => {
console.log("Something went wrong, or this answer sucks ha ha!");
});
})
}
// Or using async/await, for fun
async someBusinessLogicFunction2 () {
let yourString = "";
try {
yourString = await getStuffAsync();
} catch (e) {
console.log("Something went wrong, or this answer sucks ha ha!");
}
}
My example splits out your HTTP request into one function, with another function declared, which calls that function, and performs the promise chaining. You could omit the second function, and return the chained promise from the function that performs the HTTP request.
You have something like this (got it from your code block before you edited the question)
const promise = axios
.post(url("fistbump"), data)
.then(result => {
window.console.log("Got fistbump response: ", result.data);
localStorage.setItem(ACCOUNT_TOKEN_FIELD, result.data.key);
});
return promise;
If the Axios promise respects the ES6 promise spec, you can simply return what you want from the .then clause to get the value wrapped in a promise, which gives you
const promise = axios
.post(url("fistbump"), data)
.then(result => {
window.console.log("Got fistbump response: ", result.data);
localStorage.setItem(ACCOUNT_TOKEN_FIELD, result.data.key);
return result.data;
});
return promise;
I have the following code that uses callbacks inside promises:
const clue = 'someValue';
const myFunction = (someParam, callback) => {
someAsyncOperation(someParam) // this function returns an array
.then((array) => {
if (array.includes(clue)){
callback(null, array); // Callback with 'Success'
}
else{
callback(`The array does not includes: ${clue}`); // Callback with Error
}
})
.catch((err) => {
// handle error
callback(`Some error inside the promise chain: ${err}`) // Callback with Error
})
}
and call it like this:
myFunction (someParam, (error, response) => {
if(error) {
console.log(error);
}
else {
// do something with the 'response'
}
})
Reading some documentation, I found that there is some improved way to do this:
const myFunction = (someParam, callback) => {
someAsyncOperation(someParam) // this function returns an array
.then((array) => {
if (array.includes(clue)){
callback(array);
}
else{
callback(`The array does not includes: ${clue}`);
}
}, (e) => {
callback(`Some error happened inside the promise chain: ${e}`);
})
.catch((err) => {
// handle error
callback(`Some error happened with callbacks: ${err}`)
})
}
My question:
In the sense of performance or best practices, it's okay to call the 'callback' function inside the promise as the two ways show or I'm doing something wrong, I mean some promise anti-pattern way ?
This seems really backwards and takes away from the benefits of promises managing errors and passing them down the chain
Return the asynchronous promise from the function and don't interrupt it with callbacks. Then add a catch at the end of the chain
const myFunction = (someParam) => {
// return the promise
return someAsyncOperation(someParam) // this function returns an array
.then((array) => {
return array.includes(clue) ? array : [];
});
}
myFunction(someParam).then(res=>{
if(res.length){
// do something with array
}else{
// no results
}
}).catch(err=>console.log('Something went wrong in chain above this'))
Do not use callbacks from inside of promises, that is an anti-pattern. Once you already have promises, just use them. Don't "unpromisify" to turn them into callbacks - that's moving backwards in code structure. Instead, just return the promise and you can then use .then() handlers to set what you want the resolved value to be or throw an error to set what you want the rejected reason to be:
const clue = 'someValue';
const myFunction = (someParam) => {
return someAsyncOperation(someParam).then(array => {
if (!array.includes(clue)){
// reject promise
throw new Error(`The array does not include: ${clue}`);
}
return array;
});
}
Then, the caller would just do this:
myFunction(someData).then(array => {
// success
console.log(array);
}).catch(err => {
// handle error here which could be either your custom error
// or an error from someAsyncOperation()
console.log(err);
});
This gives you the advantage that the caller can use all the power of promises to synchronize this async operation with any others, to easily propagate errors all to one error handler, to use await with it, etc...
Actually, I don't know how to check this on real Promise super class, so I'm testing it with a class extending it. But it seems to me the result would be the same.
class PromisePlus extends Promise {
constructor (handler) {
super(handler)
console.log('new promise created')
}
}
new PromisePlus((resolve, reject) => {
resolve()
}).then(() => {
return 'GOOD'
}).then(msg => {
console.log(msg)
})
prints 'new promise created' 3 times
new PromisePlus((resolve, reject) => {
resolve()
}).then(() => {
return PromisePlus.resolve('BAD')
}).then(msg => {
console.log(msg)
})
prints 'new promise created' 5 times.
The same result with returning new Promise inside then handler
new PromisePlus((resolve, reject) => {
resolve()
}).then(() => {
return new PromisePlus((resolve, reject) => {
resolve('BAD')
})
}).then(msg => {
console.log(msg)
})
Obviously, PromisePlus.resolve() creates new promise, but why does returning PromisePlus.resolve() inside then cause one more extra promise creation?
Why does returning PromisePlus.resolve() inside then cause one more extra promise creation?
Because when you are returning a promise from a then callback, it does get awaited to resolve the outer promise (the one returned from .then()). To await this inner promise, .then(…) gets called on it - and this call does create a new promise as well.
(Yes, all this is horribly inefficient, which is probably the #1 reason why promise subclassing should be avoided).
'use strict';
Promise.resolve(() => 'John')
.then((args) => {
console.log(args);
throw new Error('ops')
})
.catch((ex) => {
console.log(ex)
})
.then(() => {
throw new Error('ups')
console.log('Doe')
})
I think console.log(args); should output 'John', but when I run this code, the output is [ [Function] ]
So I am confused.
Promise.resolve will create a new Promise resolved with the value you pass to it. So, in your case, your promise is actually resolved with the function object. It means that, the then handler is passed the function object itself.
What you should have done is
new Promise((resolve, reject) => resolve('John'))
.then(args => {
console.log(args);
throw new Error('ops')
})
.catch(console.log.bind(console));
Now, you are creating a Promise object and you are resolving that with the value John.
If you want your Promise to be resolved with a value readily, then don't pass the function object but pass the actual value itself to Promise.resolve function.
Promise.resolve('John')
.then(args => {
console.log(args);
throw new Error('ops')
})
.catch(console.log.bind(console));
Now, you have a Promise, resolved with value John and the then handler will get the resolved value John.
Note: This is the recommended way of creating a Promise when you know the actual way to resolve with readily, so that you can avoid the Promise constructor anti-pattern.
'use strict';
Promise.resolve('John')
.then((args) => {
console.log(args);
throw new Error('ops')
})
.catch((ex) => {
console.log(ex)
})
.then(() => {
throw new Error('ups')
console.log('Doe')
})
I modify Promise.resolve('John'), it works.
Please see the Promise.resolve.
resolve is used to pass the arguments directly to the then-handler
if you want 'John', you need to call the anonymous function in your call to resolve()
Promise.resolve(function(){return 'John';}());
notice the }() function call.