Can I use a promise in .then arguments? - javascript

I have 2 functions:
the first one make an http post to get an xml string
function post(url, formData) {
return new Promise(function (resolve, reject) {
// make an http post and get results (xml string)
request(url, formData, function(error, xml) {
if (error) reject(error)
resolve(xml)
})
})
}
the second transform that xml to an object
function xmlToObject(xml) {
return new Promise( function (resolve, reject) {
// transform xml string to an object using xml2js for example
xml2js(xml, function(error, obj) {
if (error) reject(error)
resolve(obj)
})
})
}
Now I want to call post request and get an xml string and then transform it to an object, so which one is correct and why:
post(url, formData).then( function (xml) {
xmlToObject(xml).then( function (obj) {
// do some work
})
})
Or
post(url, formData).then( function (xml) {
xmlToObject(xml).then( function (obj) {
return obj
})
}).then( function (obj) {
// do some work
})
Or
post(url, formData).then( function (xml) {
return xmlToObject(xml)
}).then( function (obj) {
// do some work
})
Or
post(url, formData).then( xmlToObject )
.then( function (obj) {
// do some work
})

promise.then(function(obj) {
return obj
})
is pointless and should be replaced by just promise (omitting the then call).
….then(function(xml) {
xmlToObject(xml)
// ^ note the missing `return`
}).then(function(obj) {
…
});
is the only one in your collection that does not work. If you don't return anything, nothing can be awaited (and no result be obtained), and the next callback in the chain will be invoked immediately with undefined.
post(url, formData).then(function(xml) {
xmlToObject(xml).then(function(obj) {
// do some work
})
})
That does work but is cumbersome. You'll get the indentation pyramid of doom, and by neglecting to return anything from the then callbacks you make it impossible to chain anything to the outer promise. This is especially important for handling errors at the end of the chain.
The correct way of chaining promises is therefore
post(url, formData).then(function(xml) {
return xmlToObject(xml);
}).then(function (obj) {
// do some work
})
which is exactly equivalent to the shorter
post(url, formData).then(xmlToObject).then(function(obj) {
// do some work
});
If you want to do some work using both xml and obj, nesting (with return!) is an option out of many.

You can construct your promise chain w/ those above functions in the following way:
post(url, formData)
.then(xmlToObject)
.then(doSomeWork);
function doSomeWork(obj) {
//do some work
}

this one is correct
post(url, formData).then( function (xml) {
return xmlToObject(xml)
}).then( function (obj) {
// do some work
});
Reason:
when return a promise in promise resolver,You can just continue add .then handler.Maybe promise is designed to use this feature to avoid callback hell

i prefer for clear chaining this variant:
return Promise.resolve()
.then(function () {
return post(url, formData);
})
.then(function (xml) {
return xmlToObject(xml);
})
.then(function (object) {
return something;
});

Related

How to properly write es5 Promise JavaScript AngularJs

Hi I am trying to write this function as a promise, I know how to write async functions in es6 but in es5 I am strugling
This is wath I tried:
function getLeagueByID(sportID) {
console.log(sportID);
new Promise(function (resolve, reject) {
RulesService.getLeagueByID(sportID)
.then(function (results) {
$scope.leagueById = results.data;
getStatistics($scope.leagueById[0]);
})
.catch(function (err) {
console.log(err);
});
});
}
And how should I call this function latter?
Three notes:
Whenever you already have a promise (in this case, from RulesService.getLeagueByID) you don't need to use new Promise. Insetad, chain off the promise you already have. (More here.)
Don't swallow errors; either allow them to propagate to the caller so the caller knows what's going on, or report them (not just to the console). Only handle errors at the highest level you can, which is usually an event handler that kicked off the overall process.
You aren't returning anything from the function.
Addressing those, your function should probably look like this, which allows the caller to deal with errors:
function getLeagueByID(sportID) {
console.log(sportID);
return RulesService.getLeagueByID(sportID)
.then(function(results) {
$scope.leagueById = results.data;
getStatistics($scope.leagueById[0]); // If this returns a promise or
// value you should return, add
// `return` in front of it
});
});
}
But if you don't expect the caller to handle errors, then:
function getLeagueByID(sportID) {
console.log(sportID);
return RulesService.getLeagueByID(sportID)
.then(function(results) {
$scope.leagueById = results.data;
getStatistics($scope.leagueById[0]); // If this returns a promise or
// value you should return, add
// `return` in front of it
})
.catch(function (err) {
// Do something other than just `console.log` here, in most cases; for instance, show an
// error to the user
});
});
}
And how should I call this function latter?
That depends on what's calling the function. As with the above, if it's something that's expecting its caller to handle errors, then:
return getLeagueByID(someSportId);
If it's an event handler or similar, and so doesn't expect its caller to handle errors, then:
getLeagueByID(someSportId)
.catch(function (err) {
// Do something other than just `console.log` here, in most cases; for instance, show an
// error to the user
});
As RulesService.getLeagueByID returns a promise, you can just return that.
function getLeagueByID(sportID) {
console.log(sportID);
return RulesService.getLeagueByID(sportID)
.then(function (results) {
$scope.leagueById = results.data;
getStatistics($scope.leagueById[0]);
})
.catch(function (err) {
console.log(err);
});
}
To call it
getLeagueByID(id)
.then(function(){HANDLER CODE});
function getLeagueByID(sportID) {
console.log(sportID);
return new Promise(function (res, rej) { //you need to return here
RulesService.getLeagueByID(sportID)
.then(function (results) {
$scope.leagueById = results.data;
res(getStatistics($scope.leagueById[0])); //resolve
return; //return to avoid unintended side effects
})
.catch(function (err) {
console.log(err);
rej(err) //need to reject
return; //return to avoid unintended side effects
});
});
}
A promise is just like an async method.
To use this, you can
await getLeagueByID(sportID)
in an async method, or you can use
getLeagueByID(sportID).then(r=>console.log(r)).catch(e=>console.log(e))
essentially 'forking' the current thread.

How to syncronize order of chained jQuery promised

I'm chaining 3 ajax requests with to a RESTful endpoint:
PUT some data (return {} and OK)
GET the data I just put
show data
I've set up a chain of promises using .then(). But the request does not happen in the expected order (1,2,3) but rather (2,1) and starting with a OPTIONSrequest.
Why are they not happening in the expected order?
How can I ensure the correct sequential order?
var _id = x;
function doReqs() {
putData(_id, data)
.then(getData(_id))
.then(showData);
}
// returns empty object {}
function putData(id, data) {
return $.ajax({
method: 'PUT',
url: http://xxx,
contentType: 'application/json'
});
}
// returns JSON {"data": {"xx": "xx}}
function getData(id) {
return $.ajax({
method: 'GET',
url: http://xxx
});
}
function showData(data) {
console.log(data);
}
In this code:
function doReqs() {
putData(_id, data)
.then(getData(_id))
.then(showData);
}
The .then(getData(_id)) part is just wrong. It's wrong for two reasons.
.then() is supposed to be passed a function reference. When you pass getData(_id), you are executing that function immediately and passing the return value from that function (which is a jqXHR object) to .then(). That's not what you're supposed to pass to .then().
Because you're executing getData(_id) IMMEDIATELY, it will not execute properly in the promise chain sequence.
Remember, any time you pass a func() with the parens after it as an argument, it executes that function immediately and passes it's return value as the argument. That is NOT what you want with .then() for the above reasons.
If you're trying to control what is being passed to getData(), then you can either make sure the right thing is returned from putData() because that's what will be passed to getData() or you can make a stub function that will pass the right thing:
function doReqs() {
putData(_id, data)
.then(function() {
return getData(_id);
})
.then(showData);
}
Or, you could do it this way:
function doReqs() {
putData(_id, data)
.then(getData.bind(null, _id))
.then(showData);
}
Or, since the resolved value of putData() is what will be passed as an argument to the next step in the promise chain (which is getData), you could do this:
function putData(id, data) {
return $.ajax({
method: 'PUT',
url: http://xxx,
contentType: 'application/json'
}).then(function() {
// make sure putData passes the id to the next step in the chain
return id;
});
}
function doReqs(id) {
putData(id, data)
.then(getData)
.then(showData);
}
Here's a working example of chaining in action:
function delay(t, val) {
return new Promise(function(resolve) {
setTimeout(resolve.bind(null, val), t);
});
}
function first(arg) {
console.log("running first..., arg = ", arg);
return delay(500, 10);
}
function second(arg) {
console.log("running second..., arg = ", arg);
return delay(100, 100);
}
function third(arg) {
console.log("running third..., arg = ", arg);
}
first(1).then(second).then(third);

JS promise passing between functions / wait for promises

I am trying to work through JS Promises in node.js and don't get the solution for passing promises between different function.
The task
For a main logic, I need to get a json object of items from a REST API. The API handling itself is located in a api.js file.
The request to the API inthere is made through the request-promise module. I have a private makeRequest function and public helper functions, like API.getItems().
The main logic in index.js needs to wait for the API function until it can be executed.
Questions
The promise passing kind of works, but I am not sure if this is more than a coincidence. Is it correct to return a Promise which returns the responses in makeRequest?
Do I really need all the promises to make the main logic work only after waiting for the items to be setup? Is there a simpler way?
I still need to figure out, how to best handle errors from a) the makeRequest and b) the getItems functions. What's the best practice with Promises therefor? Passing Error objects?
Here is the Code that I came up with right now:
// index.js
var API = require('./lib/api');
var items;
function mainLogic() {
if (items instanceof Error) {
console.log("No items present. Stopping main logic.");
return;
}
// ... do something with items
}
API.getItems().then(function (response) {
if (response) {
console.log(response);
items = response;
mainLogic();
}
}, function (err) {
console.log(err);
});
api.js
// ./lib/api.js
var request = require('request-promise');
// constructor
var API = function () {
var api = this;
api.endpoint = "https://api.example.com/v1";
//...
};
API.prototype.getItems = function () {
var api = this;
var endpoint = '/items';
return new Promise(function (resolve, reject) {
var request = makeRequest(api, endpoint).then(function (response) {
if (200 === response.statusCode) {
resolve(response.body.items);
}
}, function (err) {
reject(false);
});
});
};
function makeRequest(api, endpoint) {
var url = api.endpoint + endpoint;
var options = {
method: 'GET',
uri: url,
body: {},
headers: {},
simple: false,
resolveWithFullResponse: true,
json: true
};
return request(options)
.then(function (response) {
console.log(response.body);
return response;
})
.catch(function (err) {
return Error(err);
});
}
module.exports = new API();
Some more background:
At first I started to make API request with the request module, that works with callbacks. Since these were called async, the items never made it to the main logic and I used to handle it with Promises.
You are missing two things here:
That you can chain promises directly and
the way promise error handling works.
You can change the return statement in makeRequest() to:
return request(options);
Since makeRequest() returns a promise, you can reuse it in getItems() and you don't have to create a new promise explicitly. The .then() function already does this for you:
return makeRequest(api, endpoint)
.then(function (response) {
if (200 === response.statusCode) {
return response.body.items;
}
else {
// throw an exception or call Promise.reject() with a proper error
}
});
If the promise returned by makeRequest() was rejected and you don't handle rejection -- like in the above code --, the promise returned by .then() will also be rejected. You can compare the behaviour to exceptions. If you don't catch one, it bubbles up the callstack.
Finally, in index.js you should use getItems() like this:
API.getItems().then(function (response) {
// Here you are sure that everything worked. No additional checks required.
// Whatever you want to do with the response, do it here.
// Don't assign response to another variable outside of this scope.
// If processing the response is complex, rather pass it to another
// function directly.
}, function (err) {
// handle the error
});
I recommend this blog post to better understand the concept of promises:
https://blog.domenic.me/youre-missing-the-point-of-promises/

Calling another prototype method after promise completed

I have following code in my node.js file;
GameHelperAuth.prototype.GetUserViaApi = Promise.method(function (authCookie, callback) {
// get user from API
});
GameHelperAuth.prototype.GetObjectFromCache = Promise.method(function (authCookie, callback) {
// get user from Cache
});
GameHelperAuth.prototype.GetUser = function (authCookie, callback) {
// check cache
this.GetObjectFromCache()
.then(function (result) {
if (result) {
return callback(null, result);
}
else {
// not found in cache, get it from API
// **NOT WORKING HERE - undefined error**
this.GetUserViaApi(authCookie)
.then(function (apiResult) {
return callback(null, apiResult);
}).catch(function (err) {
throw err;
});
}
})
.catch(function (err) {
throw err;
});
I would like to access my instance method from another instance method once promise is completed. But it looks like it loses it's context and cannot find function anymore. (Please see where I am calling GetUserViaApi method)
Is there any way for me to reach that method without creating new instance of my class?
As far as I can see, the simplest fix here is to just declare var self = this in the first line of .GetUser() and then use self instead of this inside the .then callback.
Alternatively if you're using Node 4+ with ES6 compatibility, use an "arrow function" as the outer .then callback that inherits the lexical this instead of contextual this:
return this.GetObjectFromCache()
.then((result) => {
if (result) {
return callback(null, result);
} else {
// not found in cache, get it from API
return this.GetUserViaApi(authCookie)
.then(function (apiResult) {
return callback(null, apiResult);
}).catch(function (err) {
throw err;
});
}
})
.catch(function (err) {
throw err;
});
NB: note the addition of the return in the first line and in the else clause, necessary to ensure that the function and that branch both correctly return a promise.
FWIW, I also think you can refactor this substantially by eliminating the repeated call to return callback(...) through a chaining .then:
GameHelperAuth.prototype.GetUser = function (authCookie, callback) {
return this.GetObjectFromCache()
.then(result => result || this.GetUserViaApi(authCookie))
.then(result => callback(null, result));
}
I've removed both .catch blocks - doing .catch(function(err) { throw err }) is a no-op - AIUI the throw would make the caller end up in their own .catch block so you might just as well let the entire promise reject anyway.

Return 2 variables using bluebird promises

I am trying to write a promise function using Bluebird library for nodejs. I want to return 2 variables from my function.
I want the first function to return immediately and the second to complete its own promise chain before returning.
function mainfunction() {
return callHelperfunction()
.then(function (data) {
//do something with data
//send 200 Ok to user
})
.then(function (data2) {
//wait for response from startthisfunction here
})
.catch(function (err) {
//handle errors
});
}
function callHelperfunction() {
return anotherHelperFunction()
.then(function (data) {
return data;
return startthisfunction(data)
.then(function () {
//do something more!
})
});
}
Just like regular functions only have one return value, similarly promises only resolve with one value since it's the same analogy.
Just like with regular functions, you can return a composite value from a promise, you can also consume it using .spread for ease if you return an array:
Promise.resolve().then(function(el){
return [Promise.resolve(1), Promise.delay(1000).return(2));
}).spread(function(val1, val2){
// two values can be accessed here
console.log(val1, val2); // 1, 2
});
The only thing that appears to be wrong is the expectation that do something with data; send 200 Ok to user; should be performed in mainfunction(), part way through the promise chain in callHelperfunction().
This can be overcome in a number of ways. Here's a couple :
1. Move do something with data; send 200 Ok to user; into callHelperfunction()
function mainfunction() {
return callHelperfunction())
.catch(function (err) {
//handle errors
});
}
function callHelperfunction() {
return anotherHelperFunction()
.then(function (data1) {
//do something with data
//send 200 Ok to user
return startthisfunction(data1)
.then(function (data2) {
//wait for response from startthisfunction here
//do something more!
});
});
}
2. Dispense with callHelperfunction() altogether and do everything in mainfunction()
function mainfunction() {
return anotherHelperFunction()
.then(function (data1) {
//do something with data1
//send 200 Ok to user
return startthisfunction(data1);
})
.then(function (data2) {
//wait for response from startthisfunction here
})
.catch(function (err) {
//handle errors
});
}

Categories