Chaining Angular promise rejections - javascript

I have a chained promise and in the case of a rejection for either of the promises, I need to perform an async operation (get the translated error message). As I've already got a chained promise on success, I assume it's not possible to also chain on rejection - I am attempting to simply nest the async calls, but I'm not getting the resolved promise back from deferred.reject(deferredRejection.promise); below. Pointers appreciated!
login: function(email, password) {
var deferred = $q.defer();
AuthService.login(email, password).then(function(response) {
var user = {
'accountToken': response.accountToken,
'email': response.username,
'onboarded': response.onboarded,
'verified': response.verified
};
return SyncStorageService.write(SyncStorageService.storageKeys.user,
user);
}, function(error) {
// login failed
var deferredRejection = $q.defer();
$translate('ALERTS.LOGIN_FAILED').then(function(translatedValue) {
deferredRejection.resolve(translatedValue);
});
deferred.reject(deferredRejection.promise);
}).then(function(data) {
deferred.resolve(data);
}, function(error) {
// saving data failed
var deferredRejection = $q.defer();
$translate('ALERTS.UNKNOWN').then(function(translatedValue) {
deferredRejection.resolve(translatedValue);
});
deferred.reject(deferredRejection.promise);
});
return deferred.promise;
}
Updated Solution:
Based on the answer below, I was able to re-write the code as follows:
login: function(email, password) {
return AuthService.login(email, password).then(function(response) {
return {
'accountToken': response.accountToken,
'email': response.username,
'onboarded': response.onboarded,
'verified': response.verified
};
}).then(function(data) {
return SyncStorageService.write(SyncStorageService.storageKeys.user,
data);
});
}
Notes:
Both AuthService.login and SyncStorageService.write now reject promises with an Error object (e.g. new Error('ALERT.ERROR_MESSAGE');), which bubbles up through login to the controller (previously was doing the translation at the service level);
The controller that calls the login method has .then() and .catch() blocks - on a .catch(), the passed Error.message is translated and displayed.

It looks like you're not really chaining promises, and using the forgotten promise/deferred anti pattern. Making a few assumptions about how you actually want it to behave, and factoring out the calls to $translate, then something like the following I suspect is what you're after:
login: function(email, password) {
return AuthService.login(email, password).then(function(response) {
return {
'accountToken': response.accountToken,
'email': response.username,
'onboarded': response.onboarded,
'verified': response.verified
};
}, function() {
return $q.reject('ALERTS.LOGIN_FAILED');
}).then(function(user) {
return SyncStorageService.write(SyncStorageService.storageKeys.user, user).catch(function() {
return $q.reject('ALERTS.UNKNOWN');
});
}).catch(function(message) {
return $translate(message).then(function(translatedValue) {
return $q.reject(translatedValue);
});
});
}
The main things to keep in mind are:
If you definitely want to reject the derived promise, return $q.reject(error) from the success or error callback.
All the error callbacks above do this. The ones after attempted login or saving use the translation key as the error that will be eventually passed to the final catch callback. The success callback from $translate also does this to transform its resolved promise to a rejected one, so the final catch callback returns a rejected promise, so the calling code gets a rejected promise, and (presumably) shows the translated error to the user.
If you definitely want to resolve the derived promise, return anything that isn't a promise from he success or error callbacks. The derived promise will be resolved with that value. (This includes undefined if you don't have an explicit return value).
This is what is done above when returning the user return {'accountToken'.... in the first callback.
If you want to defer the resolution/rejection of a promise, return another promise from the success or error callback. Then the derived promise will be eventually resolved or rejected in whatever manner this other promise is resolved/rejected.
This is what's done above when returning SyncStorageService.write..., and when returning $translate(....

Related

Intentionally not returning Bluebird Promise

I have the piece of code below. Want to call the callback which may return a promise. Resolve it. In case if the promise failed, log it. The caller should NOT know about all this and should return without waiting for the promise to fulfill. That's why I'm not returning the promise. This causes the following error:
(node:21146) Warning: a promise was created in a handler at internal/timers.js:456:21 but was not returned from it, see http goo.gl/rRqMUw
at Function.Promise.cast (bluebird/js/release/promise.js:225:13)
I've read the docs and they recommend returning null to prevent the warning from happening. Nevertheless, the warning still pops out. Also, do not want to disable the warning globally.
private _trigger(cb : () => Resolvable<any>)
{
try
{
const res = cb();
const prom = Promise.resolve(res);
prom.catch(reason => {
this.logger.error("ERROR: ", reason);
})
}
catch(reason)
{
this.logger.error("ERROR: ", reason);
}
return null;
}
The internal Promise should resolve to a value of null to silence the warning - this will tell Bluebird "The Promise isn't being returned, but since it resolves to null, it doesn't contain a useful result, so this is deliberate"; returning it wouldn't give the caller useful data. You can do something like:
const prom = Promise.resolve(res)
.catch(reason => {
this.logger.error("ERROR: ", reason);
})
.then(() => null);

javascript not resolving promise

Javascript code, trying to resolve a promise immediately:
var promiseData;
var promise = <<<promise maker>>>.then(function (myContent) {
console.log("success");
}, function () {
console.log("fail!");
});
Promise.resolve(promise)
console.log("about to return");
return promiseData;
which output's to the console:
about to return
success
I have a requirement to make the promise return immediately (the promise is being created in a callback method, and the method needs to return a value immediately, returning the data later means we are no longer in the correct context and the value (that has not yet been returned) has already been used (as undefined).
Any suggestions of what I might be doing wrong?
Update:
<<<promise maker>>> is a call to a dependency that returns a promise;
It looks like you expect Promise.resolve(promise) to immediately halt, wait until the promise is resolved, and continue afterwards. That would come close to synchronous execution.
However, Promise.resolve(value) returns a Promise that immediately resolves with value, it does not resolve an existing Promise.
What you're looking for is await (or just Promise.then):
var promise = <<<promise maker>>>.then(function (myContent) {
console.log("success");
}, function () {
console.log("fail!");
});
promise.then(function() {
console.log("about to return");
});
You might observe that I left out the promiseData in the snippet. That's because in order to return the data at the right moment, you have to be asynchronous there as well. So you have to actually return a Promise that will resolve with promiseData, it comes down to:
<<<promise maker>>>
.then(function(promiseData) {
console.log('success');
return promiseData;
})
.then(function(promiseData) {
console.log('about to return');
return promiseData;
})
.catch(function(err) { console.log('fail!'); })
If I am not wrong, this function flow is near what you need, but it also returns a promise resolved into promiseData:
async function getPromiseData() {
try {
const promiseData = await <<<promise maker>>>;
console.log("success");
console.log("about to return");
return promiseData;
} catch (err) {
console.log("fail!");
}
}

Extracting object returned from promise to push to an array, returning as Promise {<pending>} with [[PromiseValue]] object [duplicate]

My code:
let AuthUser = data => {
return google.login(data.username, data.password).then(token => { return token } )
}
And when i try to run something like this:
let userToken = AuthUser(data)
console.log(userToken)
I'm getting:
Promise { <pending> }
But why?
My main goal is to get token from google.login(data.username, data.password) which returns a promise, into a variable. And only then preform some actions.
The promise will always log pending as long as its results are not resolved yet. You must call .then on the promise to capture the results regardless of the promise state (resolved or still pending):
let AuthUser = function(data) {
return google.login(data.username, data.password).then(token => { return token } )
}
let userToken = AuthUser(data)
console.log(userToken) // Promise { <pending> }
userToken.then(function(result) {
console.log(result) // "Some User token"
})
Why is that?
Promises are forward direction only; You can only resolve them once. The resolved value of a Promise is passed to its .then or .catch methods.
Details
According to the Promises/A+ spec:
The promise resolution procedure is an abstract operation taking as
input a promise and a value, which we denote as [[Resolve]](promise,
x). If x is a thenable, it attempts to make promise adopt the state of
x, under the assumption that x behaves at least somewhat like a
promise. Otherwise, it fulfills promise with the value x.
This treatment of thenables allows promise implementations to
interoperate, as long as they expose a Promises/A+-compliant then
method. It also allows Promises/A+ implementations to “assimilate”
nonconformant implementations with reasonable then methods.
This spec is a little hard to parse, so let's break it down. The rule is:
If the function in the .then handler returns a value, then the Promise resolves with that value. If the handler returns another Promise, then the original Promise resolves with the resolved value of the chained Promise. The next .then handler will always contain the resolved value of the chained promise returned in the preceding .then.
The way it actually works is described below in more detail:
1. The return of the .then function will be the resolved value of the promise.
function initPromise() {
return new Promise(function(res, rej) {
res("initResolve");
})
}
initPromise()
.then(function(result) {
console.log(result); // "initResolve"
return "normalReturn";
})
.then(function(result) {
console.log(result); // "normalReturn"
});
2. If the .then function returns a Promise, then the resolved value of that chained promise is passed to the following .then.
function initPromise() {
return new Promise(function(res, rej) {
res("initResolve");
})
}
initPromise()
.then(function(result) {
console.log(result); // "initResolve"
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("secondPromise");
}, 1000)
})
})
.then(function(result) {
console.log(result); // "secondPromise"
});
I know this question was asked 2 years ago, but I run into the same issue and the answer for the problem is since ES2017, that you can simply await the functions return value (as of now, only works in async functions), like:
let AuthUser = function(data) {
return google.login(data.username, data.password)
}
let userToken = await AuthUser(data)
console.log(userToken) // your data
The then method returns a pending promise which can be resolved asynchronously by the return value of a result handler registered in the call to then, or rejected by throwing an error inside the handler called.
So calling AuthUser will not suddenly log the user in synchronously, but returns a promise whose then registered handlers will be called after the login succeeds ( or fails). I would suggest triggering all login processing by a then clause of the login promise. E.G. using named functions to highlight the sequence of flow:
let AuthUser = data => { // just the login promise
return google.login(data.username, data.password);
};
AuthUser(data).then( processLogin).catch(loginFail);
function processLogin( token) {
// do logged in stuff:
// enable, initiate, or do things after login
}
function loginFail( err) {
console.log("login failed: " + err);
}
If that situation happens for a multiple values like an array.
[
Promise { <pending> },
Promise { <pending> },
Promise { <pending> },
Promise { <pending> },
Promise { <pending> }
]
You can use Promise.all() this will resolve all promises.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
See the MDN section on Promises. In particular, look at the return type of then().
To log in, the user-agent has to submit a request to the server and wait to receive a response. Since making your application totally stop execution during a request round-trip usually makes for a bad user experience, practically every JS function that logs you in (or performs any other form of server interaction) will use a Promise, or something very much like it, to deliver results asynchronously.
Now, also notice that return statements are always evaluated in the context of the function they appear in. So when you wrote:
let AuthUser = data => {
return google
.login(data.username, data.password)
.then( token => {
return token;
});
};
the statement return token; meant that the anonymous function being passed into then() should return the token, not that the AuthUser function should. What AuthUser returns is the result of calling google.login(username, password).then(callback);, which happens to be a Promise.
Ultimately your callback token => { return token; } does nothing; instead, your input to then() needs to be a function that actually handles the token in some way.
Your Promise is pending, complete it by
userToken.then(function(result){
console.log(result)
})
after your remaining code.
All this code does is that .then() completes your promise & captures the end result in result variable & print result in console.
Keep in mind, you cannot store the result in global variable.
Hope that explanation might help you.
I had the same issue earlier, but my situation was a bit different in the front-end. I'll share my scenario anyway, maybe someone might find it useful.
I had an api call to /api/user/register in the frontend with email, password and username as request body. On submitting the form(register form), a handler function is called which initiates the fetch call to /api/user/register. I used the event.preventDefault() in the beginning line of this handler function, all other lines,like forming the request body as well the fetch call was written after the event.preventDefault(). This returned a pending promise.
But when I put the request body formation code above the event.preventDefault(), it returned the real promise. Like this:
event.preventDefault();
const data = {
'email': email,
'password': password
}
fetch(...)
...
instead of :
const data = {
'email': email,
'password': password
}
event.preventDefault();
fetch(...)
...
Try this
var number1 = document.getElementById("number1");
var number2 = document.getElementById("number2");
startAsync.addEventListener("click", function() {
if (number1.value > 0 && number2.value > 0) {
asyncTest(parseInt(number1.value), parseInt(number2.value)).then(function(result) {
document.getElementById("promiseResolved").textContent = "promiseResolved: " + result
});
} else {
asyncTest(1, 2).then(function(result) {
document.getElementById("promiseResolved").textContent = "promiseResolved: " + result
});
}
});
async function asyncTest(a, b) {
return await (a + b);
};
<button id="startAsync">start Async function</button><br />
<input type="number" id="number1" /><br />
<input type="number" id="number2" /><br />
<span id="promiseResolved"></span><br />
Im my case (JS) I forgot to add await

AngularJS deferred.reject not working but $q.reject working

I am confused between Angular JS deferred and $q. I found this SO Question that explains the difference between $q.defer() and $q.It explains
$q.reject is a shortcut to create a deferred and then reject it immediately
So $q.reject() must be equal to
var deferred = $q.defer(); deferred.reject(), if not please explain the actual difference between the two.
But in my case, $q.reject() is working, but deffered.reject() is not working. Also we need to return rejected promised like $q.reject() but not deferred.reject(). I have seen examples where there is no return on deffered.reject()
This is the code
var deferred = $q.defer();
myService.getData()
.then(function(response){
deferred.notify('Just a notification');
deferred.reject('rejected');
})
.then(function(response) {
console.log('done');
}, function(response) {
console.log('rejected');
})
This is not working, but when I replaced deferred.reject with $q.reject(), the promise has been rejected and the control is moved to the error function of the followed then block.
Any help is greatly appreciated. Thanks in advance.
It doesn't work when you use deferred.reject because you are not returning new rejected promise. You can use both $q.reject() and deferred.reject() you just need to return a promise in both cases.
You need to to understand that
$q.reject() is rejected promise object
deferred.reject() is not a promise, but deferred object which has rejected promise in one of its properties (namely, $promise).
So you can return any object or value and it will become a new promise object and will be passed to the next then block in chain. However, when you return deferred.reject() it will be passed as just some object (one more time, it is not a promise, but it has a promise inside) and next promise will get resolved successfully of course.
It will work properly with deferred too if you return corresponding promise:
var deferred = $q.defer();
myService.getData()
.then(function(response) {
deferred.notify('Just a notification');
deferred.reject('rejected');
return deferred.promise;
//return $q.reject();
})
.then(function(response) {
console.log('done');
}, function(response) {
console.log('rejected');
});
And finally answer to you question: $q.reject() is a promise object with status "rejected". deferred.reject() is not a promise, but it has rejected promise object inside as deferred.$promise. What to use? You should use $q.reject(), using dummy deferred object is redundant in this case and considered bad practice, in fact it even has a name as deferred anti-pattern.
Make sure you are returning a promise.
function getData() {
var deferred = $q.defer();
myService.getData()
.then(function (response) {
deferred.resolve('Just received a notification');
}).catch(function (err) {
deferred.reject(err);
};
return deferred.promise;
}
getData().then(function (response) {
console.log('done');
}, function (response) {
console.log('rejected');
});
This is working with Q (https://github.com/kriskowal/q)
var def = Q.defer();
def.promise
.then(
function(ok){
return ok;
},
function(err){
var d = Q.defer();
d.reject(err);
return d.promise;
}
)
.then(
function(ok){
console.log('ok',ok);
},
function(err){
console.log('err',err);
}
);
def.reject('error');

Node.js Q promises then() chaining not waiting

I am trying to refactor the following code to avoid the callback hell, transforming it into:
createUser(user_data)
.then(createClient(client_data))
.then(createClientPartner(clientpartner_data))
.then(function(data) {
cb(null, _.pick(data,['id','username']));
}, function(error) {
cb(error);
});
As you see, I created a method for each one of the steps:
function createUser(user_data) {
console.log('createUser');
var deferred = Q.defer()
new db.models.User.create(user_data, function(err, user) {
console.log('createUser done');
if (!!err)
deferred.reject(err);
else {
client_data['id'] = user.id;
deferred.resolve(user);
}
});
return deferred.promise;
}
The other methods have identical console.log calls to be able to follow the execution path.
I would expect it to be:
createUser
createUser done
createClient
createClient done
createClientPartner
createClientPartner done
But instead, it is:
createUser
createClient
createClientPartner
createClientPartner done
createUser done
createClient done
Why does are the functions triggered when the previous promise have not been resolved? I expect "then" to wait until previous promise have been resolved or rejected to continue. Am I missing something important about promises?
The problem is you don't pass functions, but the result of function calls.
Instead of
createUser(user_data)
.then(createClient(client_data))
you should have
createUser(user_data)
.then(function(user){
createClient(client_data) // no need for the user ? really ?
})

Categories

Resources