I am trying to use an Angular service to make a call to either use fs.readFile or fs.writeFile depending on type of button pressed in order to understand how node and angular promises interact. What I have is reading writing files, but does not send back read data, nor does it throw any errors for me to understand what has gone wrong.
//HTML
<button ng-click="rw('write')">WRITE FILE</button>
<button ng-click="rw('read')">READ FILE</button>
//angular
angular.module('test', [])
.controller('ctrl', function($scope, RWService){
$scope.rw = function(type){
RWService.rw(type)
.then(
function(res){
console.log('success');
},
function(err){
console.log('error');
})
};
})
.service('RWService',['$http', '$q', function($http, $q){
this.rw = function(type){
var promise = $http.get('./rw/' + type);
var dfd = $q.defer();
promise.then(
function(successResponse){
dfd.resolve(successResponse);
},
function(errorResponse){
dfd.reject(errorResponse);
}
);
return dfd.promise;
};
}]);
//node
var fs = require('fs')
, async = require('async')
, Q = require('Q');
var dest = './file.txt';
var rw = {
write: function(data){
data = data.repeat(5);
return Q.nfcall(fs.writeFile, dest, data);
}
, read: function(data){
data = data.repeat(5);
var deferred = Q.defer();
console.log('inside read');
fs.readFile(dest, 'utf8', function(err, data){
if (err){
deferred.reject('some error');
}else{
deferred.resolve(data);
}
});
return deferred.promise;
}
};
module.exports = exports = rw;
//node server
app.get('/rw/:type', function(req, res, next){
var type = req.params.type;
var data = 'some text string\n';
if (type == 'write'){
//omitted fro brevity
}else{
rw.read(data)
.then(function(response){
return {'response': response};
})
.catch(function(err){
return {'index.js error': err};
});
}
});
I structured the angular $q portion off of this blog post.
Here is a native Promise implementation of your code.
var fs = require('fs');
var dest = './file.txt';
var rw = {
write: function(data){
return new Promise(function (resolve, reject) {
data = data.repeat(5);
fs.writeFile(function (err, result) {
if (err) return reject(err.message);
return resolve(result);
});
});
},
read: function(data){
return new Promise(function (resolve, reject) {
data = data.repeat(5);
fs.readFile(dest, 'utf8', function(err, contents) {
if (err) return reject(err.message);
return resolve(contents.toString());
});
});
}
};
module.exports = exports = rw;
[edit: I just changed the code to put data=data.repeat(5) inside the promise factory method. Basically, if anything CAN raise an exception, you should try to put it inside that promise function, or you again run the risk of silently killing the script again.]
A couple of comments:
Returning deferred is incredibly useful, but you have to be careful about how you use it. I personally only use it if the asynchronous code cannot be wrapped in a simple function (such as a class instance that creates a promise in its constructor and resolves/rejects in different child methods). In your case, probably what is happening is that the script is failing in a way that fs.readFile() never gets called -- and so deferred.resolve() and deferred.reject() will never be reached. In cases like this, you need to use try/catch and always call deferred.reject() in there as well. It is a lot of extra work that is easily avoided.
Instead, you should try to use the vanilla standard implementation of Promises as you see above.
Lastly, Q was a groundbreaking library that basically taught the world how to do promises in the first place, but it has not been updated in years and was never particularly feature-rich or fast. If you need more features, take a look at when.js*, kew or Bluebird (note that Bluebird claims to be the fastest, but I've personally found that to be untrue.)
(*I actually loved working with when.js and find it a bit painful using dumb native promises, but hey, standards are standards.)
[edit: Adding details on the Angular side of things]
So based on your comment, here is what I suspect you are also looking for. You will see that here I am using $http.get() as the only promise. No need to use defer() once you are inside a promise, so actually there is no need to even include $q.
I'm sorry, I've never used service(). Even Angular's own documentation on creating services uses the factory() method, so that's what I'm using here.
.factory('RWService',['$http', function($http){
return {
rw: function (type) {
// $http returns a promise. No need to create a new one.
return $http.get('./rw/' + type)
.then(function (response) {
// You can do other stuff here. Here, I am returning the
// contents of the response. You could do other stuff as
// well. But you could also just omit this `then()` and
// it would be the same as returning just the response.
return response.data;
})
.catch(function (err) {
// You can do other stuff here to handle the error.
// Here I am rethrowing the error, which is exactly the
// same as not having a catch() statement at all.
throw err;
});
}
};
}]);
If you read the comments in the code above, you should realize that you can write the same code like this:
.factory('RWService',['$http', function($http){
return {
rw: function (type) {
return $http.get('./rw/' + type);
}
};
});
The only difference here is that RWService.rw() will eventually resolve the entire response object rather than the response data.
The thing to keep in mind here is that you can (and absolutely should) try to recycle your promises as much as possible. Basically, all you need to know about promises is:
every promise has a then and catch method you can wrap your logic in;
every then and catch return as a new promise;
if you throw an exception inside any then or catch, you will be thrown straight to the next catch, if there is one;
if you return a value from any then or catch, it will be passed as the argument to the very next then in the chain;
when the chain runs out of thens or you throw an exception and there are no more catches, the promise chain ends; and
then and catch are fast, but they are still asynchronous, so don't add new elements to a promise chain if you don't genuinely need them.
Related
i have two http GET in one controller and sometimes it works and two of them are working. sometime only one http Get is work. and sometimes none of them is shown.
any suggestions?
}).controller("nextSidorAdminCtrl",
function($scope,$rootScope,$http,$location,$state) {
$http.get("/ShiftWeb/rest/admin/getallsettingtime")
.then(function(response) {
$scope.settingtimes = response.data;
});
$http.get("/ShiftWeb/rest/admin/nextsidor")
.then(function(response) {
$scope.nextsidor = response.data;
});
Image:
https://prnt.sc/k5ewd6
Chain the two $http.get operations:
}).controller("nextSidorAdminCtrl",
function($scope,$rootScope,$http,$location,$state) {
$http.get("/ShiftWeb/rest/admin/getallsettingtime")
.then(function(response) {
$scope.settingtimes = response.data;
return $http.get("/ShiftWeb/rest/admin/nextsidor")
})
.then(function(response) {
$scope.nextsidor = response.data;
});
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain.
For more information, see AngularJS $q Service API Reference - chaining promises
The best way to solve this problem is to use async
In my openion, The problem in Mr. georgeawg's answer is, if $http.get("/ShiftWeb/rest/admin/getallsettingtime") will return success then $http.get("/ShiftWeb/rest/admin/nextsidor") will be called, Otherwise It will not be called.
And as I can see in the question both are independent.
So you need to follow the best way which is used async or something like that.
So your code will be :
var getAllAettingTime = function(cb) {
$http.get("/ShiftWeb/rest/admin/getallsettingtime")
.then(function(response) {
if(response.data){
$scope.settingtimes = response.data;
return cb(null,'OK');
}else{
return cb(null,'ERROR');
})
}
var nextSidor= function(cb) {
$http.get("/ShiftWeb/rest/admin/nextsidor")
.then(function(response) {
if(response.data){
$scope.nextsidor = response.data;
return cb(null,'OK');
}else{
return cb(null,'ERROR');
})
}
async.series([ getAllAettingTime, nextSidor], function(err, result) {
if (err){
/* Do what you want if err comes */
} else {
/* Do what you want if both HTTP come with success */
}
});
In the above async.series() both HTTP will be called without any dependency on each other.
For better understanding, you need study about async npm module and have to install in your app.
Let's say I have some code that looks like this:
var doSomething = function(parameter){
//send some data to the other function
return when.promise(function(resolveCallback, rejectCallback) {
var other = doAnotherThing(parameter);
//how do I check and make sure that other has resolved
//go out and get more information after the above resolves and display
});
};
var doAnotherThing = function(paramers){
return when.promise(function(resolveCallback, rejectCallback) {
//go to a url and grab some data, then resolve it
var s = "some data I got from the url";
resolveCallback({
data: s
});
});
};
How do I ensure that var other has completely resolved before finishing and resolving the first doSomething() function? I'm still wrapping my head around Nodes Async characteristic
I really didn't know how else to explain this, so I hope this makes sense! Any help is greatly appreciated
EDIT: In this example, I am deleting things from an external resource, then when that is done, going out the external resource and grabbing a fresh list of the items.
UPDATED CODE
var doSomething = function(parameter){
//send some data to the other function
doAnotherThing(parameter).then(function(){
//now we can go out and retrieve the information
});
};
var doAnotherThing = function(paramers){
return when.promise(function(resolveCallback, rejectCallback) {
//go to a url and grab some data, then resolve it
var s = "some data I got from the url";
resolveCallback({
data: s
});
});
};
The return of doAnotherThing appears to be a promise. You can simply chain a then and put your callback to utilize other. then also already returns a promise. You can return that instead.
// Do stuff
function doSomething(){
return doAnotherThing(parameter).then(function(other){
// Do more stuff
return other
});
}
// Usage
doSomething().then(function(other){
// other
});
Below is how to accomplish what you're trying to do with bluebird.
You can use Promise.resolve() and Promise.reject() within any function to return data in a Promise that can be used directly in your promise chain. Essentially, by returning with these methods wrapping your result data, you can make any function usable within a Promise chain.
var Promise = require('bluebird');
var doSomething = function(parameter) {
// Call our Promise returning function
return doAnotherThing()
.then(function(value) {
// Handle value returned by a successful doAnotherThing call
})
.catch(function(err) {
// if doAnotherThing() had a Promise.reject() in it
// then you would handle whatever is returned by it here
});
}
function doAnotherThing(parameter) {
var s = 'some data I got from the url';
// Return s wrapped in a Promise
return Promise.resolve(s);
}
You can use the async module and its waterfall method to chain the functions together:
var async = require('async');
async.waterfall([
function(parameter, callback) {
doSomething(parameter, function(err, other) {
if (err) throw err;
callback(null, other); // callback with null error and `other` object
});
},
function(other, callback) { // pass `other` into next function in chain
doAnotherThing(other, function(err, result) {
if (err) throw err;
callback(null, result);
})
}
], function(err, result) {
if (err) return next(err);
res.send(result); // send the result when the chain completes
});
Makes it a little easier to wrap your head around the series of promises, in my opinion. See the documentation for explanation.
So I've been wondering if I need to wrap things that take time in a process.nextTick or not.
For example, imagine a function that returns a promise for a xml string translated to an object.
(I've removed require and other trivialities for readability. You know what's going on here.)
// Return a promise for a javascript object
function parseXml(xml) {
var promise = q.defer();
var parser = new x2je.Parser(function(result, error) {
if (!error)
promise.resolve(result);
else
promise.reject(error);
});
parser.parseString(xml);
return promise.promise;
}
You see some people write functions like so:
// Return a promise for a javascript object
function parseXml(xml) {
var promise = q.defer();
process.nextTick(function(){
var parser = new x2je.Parser(function(result, error) {
if (!error)
promise.resolve(result);
else
promise.reject(error);
});
parser.parseString(xml);
});
return promise.promise;
}
When do I need to use process.nextTick for best practice coding?
u can get the same thing with setTimeout(function(){//},0);
It just places ur function in queue instead of stack ,mostly put to make ur code non-blocking kinda
ref these links for deeper understanding
http://howtonode.org/understanding-process-next-tick
https://www.youtube.com/watch?v=8aGhZQkoFbQ
I am trying to do multiple $http call and my code looks something like this:
var data = ["data1","data2","data3"..."data10"];
for(var i=0;i<data.length;i++){
$http.get("http://example.com/"+data[i]).success(function(data){
console.log("success");
}).error(function(){
console.log("error");
});
}
How can I have the promise to know all $http call is successfull? If anyone of it fail, will perform some action.
You could also use $q.all() method.
So, from your code:
var data = ["data1","data2","data3"..."data10"];
for(var i=0;i<data.length;i++){
$http.get("http://example.com/"+data[i]).success(function(data){
console.log("success");
}).error(function(){
console.log("error");
});
}
You could do:
var promises = [];
data.forEach(function(d) {
promises.push($http.get('/example.com/' + d))
});
$q.all(promises).then(function(results){
results.forEach(function(data,status,headers,config){
console.log(data,status,headers,config);
})
}),
This above basically means execute whole requests and set the behaviour when all have got completed.
On previous comment:
Using status you could get to know if any have gone wrong. Also you could set up a different config for each request if needed (maybe timeouts, for example).
If anyone of it fail, will perform some action.
From docs which are also based on A+ specs:
$q.all(successCallback, errorCallback, notifyCallback);
If you are looking to break out on the first error then you need to make your for loop synchronous like here: Angular synchronous http loop to update progress bar
var data = ["data1", "data2", "data3", "data10"];
$scope.doneLoading = false;
var promise = $q.all(null);
angular.forEach(data, function(url){
promise = promise.then(function(){
return $http.get("http://example.com/" + data[i])
.then(function (response) {
$scope.data = response.data;
})
.catch(function (response) {
$scope.error = response.status;
});
});
});
promise.then(function(){
//This is run after all of your HTTP requests are done
$scope.doneLoading = true;
});
If you want it to be asynchronous then: How to bundle Angular $http.get() calls?
app.controller("AppCtrl", function ($scope, $http, $q) {
var data = ["data1", "data2", "data3", "data10"];
$q.all([
for(var i = 0;i < data.length;i++) {
$http.get("http://example.com/" + data[i])
.then(function (response) {
$scope.data= response.data;
})
.catch(function (response) {
console.error('dataerror', response.status, response.data);
break;
})
.finally(function () {
console.log("finally finished data");
});
}
]).
then(function (results) {
/* your logic here */
});
};
This article is pretty good as well: http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/
Accepted answer is okay, but is still a bit ugly. You have an array of things you want to send.. instead of using a for loop, why not use Array.prototype.map?
var data = ["data1","data2","data3"..."data10"];
for(var i=0;i<data.length;i++){
$http.get("http://example.com/"+data[i]).success(function(data){
console.log("success");
}).error(function(){
console.log("error");
});
}
This becomes
var data = ['data1', 'data2', 'data3', ...., 'data10']
var promises = data.map(function(datum) {
return $http.get('http://example.com/' + datum)
})
var taskCompletion = $q.all(promises)
// Usually, you would want to return taskCompletion at this point,
// but for sake of example
taskCompletion.then(function(responses) {
responses.forEach(function(response) {
console.log(response)
})
})
This uses a higher order function so you don't have to use a for loop, looks a lot easier on the eyes as well. Otherwise, it behaves the same as the other examples posted, so this is a purely aesthetical change.
One word of warning on success vs error - success and error are more like callbacks and are warnings that you don't know how a promise works / aren't using it correctly. Promises then and catch will chain and return a new promise encapsulating the chain thus far, which is very beneficial. In addition, using success and error (anywhere else other than the call site of $http) is a smell, because it means you're relying explicitly on a Angular HTTP promise rather than any A+ compliant promise.
In other words, try not to use success/error - there is rarely a reason for them and they almost always indicate a code smell because they introduce side effects.
With regards to your comment:
I have did my own very simple experiment on $q.all. But it only trigger when all request is success. If one if it fail, nothing happen.
This is because the contract of all is that it either resolves if every promise was a success, or rejects if at least one was a failure.
Unfortunately, Angular's built in $q service only has all; if you want to have rejected promises not cause the resultant promise to reject, then you will need to use allSettled, which is present in most major promise libraries (like Bluebird and the original Q by kriskowal). The other alternative is to roll your own (but I would suggest Bluebird).
I think this is a really stupid question but I'm having a hard time wrapping my head around promises.
I'm using Q (for nodejs) to sync up a couple of async functions.
This works like a charm.
var first = function () {
var d = Q.defer();
fs.readdir(path,function(err,files){
if(err) console.log(err);
d.resolve(files);
});
return d.promise;
};
var second = function (files) {
var list = new Array;
files.forEach(function(value, index){
var d = Q.defer();
console.log('looking for item in db', value);
db.query(
'SELECT * FROM test WHERE local_name =? ', [value],{
local_name : String,
},
function(rows) {
if (typeof rows !== 'undefined' && rows.length > 0){
console.log('found item!', rows[0].local_name);
d.resolve(rows[0]);
} else {
var itemRequest = value;
getItemData(itemRequest);
}
}
);
list.push(d.promise);
});
return Q.all(list);
};
first()
.then(second)
.done(function(list){
res.send(list);
});
The problem I have is with this little function:
getItemData(itemRequest)
This function is filled with several of callbacks. The promise chain runs through the function just fine but ignores all the callbacks I use ( eg several XHR calls I make in the function).
A simplified version of the function looks like this (just to give you an idea):
function getItemData(itemRequest){
helper.xhrCall("call", function(response) {
var requestResponse = JSON.parse(response)
, requestInitialDetails = requestResponse.results[0];
downloadCache(requestInitialDetails,function(image) {
image = localImageDir+requestInitialDetails.image;
helper.xhrCall("call2", function(response) {
writeData(item,image,type, function(){
loadData(item);
});
});
} else {
writeData(item,image,type, function(){
loadData(item);
});
}
});
});
The xhr function I use looks like this:
xhrCall: function (url,callback) {
var request = require("request")
, colors = require('colors');
request({
url: url,
headers: {"Accept": "application/json"},
method: "GET"
}, function (error, response, body) {
if(!error){
callback(body);
}else{
console.log('Helper: XHR Error',error .red);
}
});
}
So my questions:
Can I leave the function unaltered and use the callbacks that are in place ánd the promise chain?
Or do I have to rewrite the function to use promises for the XHR?
And if so, How can I best write my promise chain? Should I reject the initial promise in the forEach?
Again, sorry if this is a really stupid question but I don't know what the right course of action is here.
Thanks!
[EDIT] Q.nfcall, I don't get it
So I've been looking into Q.nfcall which allows me to use node callbacks. Bu I just don't understand exacly how this works.
Could someone give a simple example how I would go about using it for a function with several async xhr calls?
I tried this but as you can see I don't really understand what I'm doing:
var second = Q.nfcall(second);
function second (files) {
[EDIT 2]
This is the final funcction in my getitemdata function callback chain. This function basically does the same as the function 'second' but I push the result directly and then return the promise. This works as stated, but without all the additional callback data, because it does not wait for the callbacks to return with any data.
function loadData(item) {
var d = Q.defer();
db.query(
'SELECT * FROM test WHERE local_name =? ', [item],{
local_name : String,
},
function(rows) {
if (typeof rows !== 'undefined' && rows.length > 0){
list.push(d.promise);
}
}
);
});
return Q.all(list);
};
Your answer is not really clear after your second edit.
First, on your orignal question, your getItemData has no influence on the promise chain.
You could change you the function's call signature and pass your deferred promise like so.
getItemData(itemRequest, d)
and pass this deferred promises all the way to your xhrCall and resolve there.
I would re-write your whole implementation and make sure all your functions return promises instead.
Many consider deferred promises as an anti-pattern. So I use use the Promise API defined in harmony (the next javascript)
After said that, I would re-implement your original code like so (I've not tested)
var Promise = Promise || require('es6-promise').Promise // a polyfill
;
function errHandler (err){
throw err
}
function makeQuery () {
var queryStr = 'SELECT * FROM test WHERE local_name =? '
, queryOpt = {local_name: String}
;
console.log('looking for item in db', value)
return new Promise(function(resolve, reject){
db.query(queryStr, [value], queryOpt, function(rows) {
if (typeof rows !== 'undefined' && rows.length > 0){
console.log('found item!', rows[0].local_name);
resolve(rows[0]);
} else {
// note that it returns a promise now.
getItemData(value).then(resolve).catch(errHandler)
}
})
})
}
function first () {
return new Promise(function(resolve, reject){
fs.readdir(path, function(err, files){
if (err) return reject(err)
resolve(files)
})
})
}
function second (files) {
return Promise.all(files.map(function(value){
return makeQuery(value)
});
}
first()
.then(second)
.then(res.send)
.catch(errHandler)
Note that there is no done method on the Promise API.
One down side of the new Promise API is error handling. Take a look at bluebird.
It is a robust promise library which is compatible with the new promise API and has many of the Q helper functions.
As far as I can tell, you need to return a promise from getItemData. Use Q.defer() as you do in second(), and resolve it when the callbacks complete with the data. You can then push that into list.
To save code, you can use Q.nfcall to immediately call a node-style-callback function, and return a promise instead. See the example in the API docs: https://github.com/kriskowal/q/wiki/API-Reference#qnfcallfunc-args