I want to chain my promises depending upon if the previous call was resolved or rejected. I am making a call to server in all promises.
So, I am writing it like-
apiServices.patientsSearch(id)
.then(function(data){
return callback(null,data);
},function(err){
return apiServices.studiesSearch(id);
}).then(function(data){
return callback(null,data);
},function(){
return apiServices.seriesSearch(id);
}).then(function(data){
return callback(null,data);
})
.catch(function(err){
return callback(false,err);
});
As every then returns a promise object, problem is that catch is always being called if any promise except the last one calls resolve. One way I am thinking is to check if err is empty and ignore it. Is it the right way to do it ?
I am using request module, if I set forever: true, I start getting-
{ [Error: socket hang up] code: 'ECONNRESET' }
With forever false, it works. Why my socket is still busy even after the request has ended ? As the next request will go only when reject is called, so socket should be free by that time.
You should only call the callback once. Don't pass it as the onfulfilled-handler after each promise, call it once in the end:
apiServices.patientsSearch(id).then(null, function(err){
return apiServices.studiesSearch(id);
}).then(null, function(){
return apiServices.seriesSearch(id);
}).then(function(data){
callback(null,data);
}, function(err){
callback(false,err);
});
or
apiServices.patientsSearch(id).catch(function(err){
return apiServices.studiesSearch(id);
}).catch(function(){
return apiServices.seriesSearch(id);
}).then(function(data){
callback(null,data);
}, function(err){
callback(false,err);
});
Of course, you shouldn't be calling any callbacks at all in promise-based code, so use this only if you have to interface with legacy code. Otherwise, don't take a callback parameter and just return the promise:
return apiServices.patientsSearch(id).catch(function(err){
return apiServices.studiesSearch(id);
}).catch(function(){
return apiServices.seriesSearch(id);
});
Related
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
I'm working with JavaScript promises, but somewhat new to them and having some issues. In the code below "todoService.addTodo(description);" is an asynchronous call to a server. However, the code does not wait for "todoService.addTodo(description);" to finish, and instead continues executing the code, which means "test" is always equal to null. I'm using promises because I thought this would help deal with the asynch calls, do I'm I simply not understanding the concept, or just doing something wrong syntactically?
let promise = new Promise(function (resolve, reject){
let test = todoService.addTodo(description);
console.log(test)
if (test) {
console.log("GOOD");
resolve("OK");
} else {
console.log("BAD");
reject("Unable to connect to server");
}
});
promise.then(function(result) {
console.log(result);
}, function(err) {
alert(err);
});
Here is the implementation of addTodo():
addTodo: function (description) {
serv.todoUrl ='http://localhost:8080/add?description=' + description;
serv.todoResource = $resource(serv.todoUrl);
serv.todoResource.save().$promise.then(function (data) {
console.log(data);
let toReturn = {result: data.result, details: data.details};
console.log(toReturn);
return toReturn;
}, function (error) {
return null;
});
If test is a promise, then if (test) is not the right way to deal with it. What's more, you should not create a new Promise when addTodo already provides the promise.
All you would need is this:
todoService.addTodo(description).then(function(result) {
console.log(result);
}, function(err) {
alert(err);
});
Now that you also added the implementation of addToDo in your question, it turns out that it lacks a return of the promise you create there, so it just returned undefined, which obviously is not a promise. Add return here:
addTodo: function (description) {
serv.todoUrl ='http://localhost:8080/add?description=' + description;
serv.todoResource = $resource(serv.todoUrl);
return serv.todoResource.save().$promise.then(function (data) {
// ^^^^^
console.log(data);
let toReturn = {result: data.result, details: data.details};
console.log(toReturn);
return toReturn;
}, function (error) {
return null;
});
Note that the other return statements are in the relative callback functions which execute asynchronously, so they don't provide the return value for addToDo, but they provide the resolving value for the promise.
NB: Since you treat the rejection case in addToDo and don't cascade the error, but just return null, addToDo will not represent a rejected promise in that case, but a fulfilled one. If you prefer to have the caller of addToDo to get a rejected promise in that case, then just remove the rejection handler function from within addToDo.
In my Controller:
function login(credentials) {
AuthService
.login(credentials)
.then(successCallback, errorCallback);
//same issue with .then(successCallback).catch(errorCallback);
}
function successCallback() {
// do something after success
}
function errorCallback(data) {
// do something after error
}
and in my AuthService:
authService.login = function (credentials) {
return $http
.post(ENV.apiEndpoint + 'api/v1/login_check', credentials)
.then(
function (result) {
Session.create(result.data.token, result.data.data);
},
function (data) {
Messages.create('Login failed: ' + data.statusText);
}
);
}
When my POST delivers a 200 response code, everything works as expected do something after success is executed.
But when my POST results e.g. in a 401 I can see that Messages.create is called (so in this case it enters the error path), but unfortunately my Controller calls the successCallback and not the errorCallback.
I had to migrate this because I was using the deprecated and since Angular 1.6 removed .success and .error promise attributes. It was working back then, but after migration this doesn't work anymore.
What am I doing wrong here?
You may reject the promise in your error callback.
authService.login = function (credentials) {
return $http
.post(ENV.apiEndpoint + 'api/v1/login_check', credentials)
.then(
function (result) {
Session.create(result.data.token, result.data.data);
},
function (data) {
Messages.create('Login failed: ' + data.statusText);
return $q.reject(data);
}
);
}
From Angular $q doc:
reject(reason);
Creates a promise that is resolved as rejected with the specified
reason. This api should be used to forward rejection in a chain of
promises. If you are dealing with the last promise in a promise chain,
you don't need to worry about it.
When comparing deferreds/promises to the familiar behavior of
try/catch/throw, think of reject as the throw keyword in JavaScript.
This also means that if you "catch" an error via a promise error
callback and you want to forward the error to the promise derived from
the current promise, you have to "rethrow" the error by returning a
rejection constructed via reject.
I am programming a new promise, it has many different conditions that call reject() or resolve() related to their state, also I know that the promise state will set with the first call to reject() | resolve().
My question is:
Is there any native (build-in) way to get the promise state?
The following is a demonstrative-code:
exports.addStatement = function (db, description, data) {
return new Promise(function (resolve, reject) {
validator.validateStatement(description, data)
.then(function (data) {
//......
if(cnd1)
resolve(res);
if(cnd2)
reject(err);
//......
//How to check if this promise is rejected or resolved yet?
})
.catch(function (err) {
reject(err);
})
})
};
You cannot directly examine the state of a promise. That's not how they work. You can use .then() or .catch() on them with a callback to get notified.
Or, in your specific case, you can probably change the way your code is structured to remove the anti-pattern of creating an unnecessary outer promise and switching your logic to if/else if/else.
Here's the cleaned up code:
exports.addStatement = function (db, description, data) {
return validator.validateStatement(description, data)
.then(function (data) {
//......
if(cnd1) {
// make res be the resolved value of the promise
return res;
} else if(cnd2) {
// make promise become rejected with err as the reason
throw err;
} else {
// decide what else to do here
}
})
})
};
If you couldn't make an if/else work for you, the above structure should still work because both the return and the throw terminate the execution of the .then() handler. So, the only code that continues after them is code that has not yet set the resolved/rejected value for the current promise so you don't have to look at the state of the promise to know that. If the code gets past the return and throw and is still executing, then neither of those was executed and the resolved/rejected value of the current promise is still unset.
I have a node.js script that opens up a Azure container, takes screenshots of a page across multiple different countries while streaming them to the Azure container. The issue I'm having is if I encounter an error in the streaming process, it finishes the remaining screenshots for that given id and then exits out of the promise chain.
So if I encounter an error at Id 211006, it completes taking all the screenshots, and then exits the stream. It doesn't continue on.
I'm very new to how promises work and how they catch errors, but my understanding is that if, say, 211006 does encounter an error, the script would complete the promise chain, and then show me any error prior to running .fin - that's not the case.
Can anybody help?
AzureService.createContainer()
.then(function () {
return ScreenshotService.getAllCountriesOfId('308572');
})
.then(function () {
return ScreenshotService.getAllCountriesOfId('211006');
})
.then(function () {
return ScreenshotService.getAllCountriesOfId('131408');
})
.then(function () {
return ScreenshotService.getAllCountriesOfId('131409');
})
.then(function () {
return ScreenshotService.getAllCountriesOfId('789927');
})
.then(function () {
return ScreenshotService.getAllCountriesOfId('211007');
})
.then(function () {
return ScreenshotService.getAllCountriesOfId('833116');
})
// Upload Log file into Azure storage
.fin(function () {
AzureService.init({
container: config.azure.storage.msft.CONTAINER.LOG,
account: config.azure.storage.msft.ACCOUNT,
key: config.azure.storage.msft.ACCESS_KEY,
file: config.file.log,
isLogFile: true
});
log.info('Utility: Uploading log file [ %s ] to Azure storage container [ %s ]', AzureService.file, AzureService.container);
return AzureService.uploadLocalFileToStorage()
.then(function () {
return util.deleteFile({fileName: AzureService.file, isLogFile: true});
})
.fail(function (err) {
log.info(err);
})
.done();
})
.fail(function (err) {
log.info(err);
})
.done();
A chain of promises is stopped anytime an error is allowed back into the chain. That sets the promise state to rejected and will call the next error handler in any subsequent .then() handlers, not the fulfilled handler.
If you want the chain to continue, then you need to catch the error. Catching the error will cause the promise infrastructure to consider it "handled" and the promise state will again be fulfilled and it will continue executing fulfilled handlers.
Promise errors are analogous to exceptions. If they are not handled, they will abort processing up until the first exception handler. If they are handled with an exception handler, then processing will continue normally after that exception handler.
In your specific case, if you want the chaing to continue, you will need to handle errors in each of these types of lines:
return ScreenshotService.getAllCountriesOfId('308572');
You can do that like this:
return ScreenshotService.getAllCountriesOfId('308572').then(null, function(err) {
console.log(err);
// error is now handled and processing will continue
});
Since you have a lot of repeated code, you should probably change your code into something that iterates through an array of country IDs rather than just copy lines of code over and over.
Here's a means of using .reduce() to chain all the promises in a loop and get rid of so much repetitive code and handle individual country errors so the chain continues:
var countryIds = ['308572', '211006', '131408', '131409', '789927', '211007', '833116'];
countryIds.reduce(function(p, item) {
return p.then(function() {
return ScreenshotService.getAllCountriesOfId(item).then(null, function(err) {
console.log(err);
});
});
}, AzureService.createContainer())
// Upload Log file into Azure storage
.fin(function () {
... rest of your code continued here