I don't understand below part
var q = require("q"),
BlogPost = require("../models/blogPost");
module.exports = {
getAllPosts: getAllPosts
};
function getAllPosts() {
var deferred = q.defer();
BlogPost
.find({})
.sort("-date")
.exec(function(error, posts) {
if (error) {
deferred.reject(error);
} else {
deferred.resolve(posts);
}
});
return deferred.promise;
}
I found above code in controller, but can't understand it. Why at the end we use return deferred.promise? How would I use the getAllPosts later? Can't we just return the posts objects?
You would consume a function that returns a promise like so:
var getAllPosts = require('./your-module-name.js').getAllPosts;
getAllPosts()
.then(function(posts) {
//successfully process the posts
})
.catch(function(err) {
//handle the error
})
.finally(function(){
//clean up anything you need to...
})
Promise is the just representation of the asynchronous result.
It has three states:
1-> success
-> Error
-> Pending
deferred is a just object of promise and it returns after processing one of above state.
We can use javascript code without promise but sometimes we have to give it to make our code execute as asynchronous way.
That's why we use promise
Related
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.
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/
I want two different controllers to run different functions after some promises are resolved in a service (i dont want this service to make an http request each time a controller needs the data, I only want one http request).
I have a service that makes a request and gets a promise. I want controller1 to see this resolution and then run some code. I then want controller2 to also see that this promise resolves and run some code (basically multiple then() methods that run on the same promise but from different files). How can I go about doing this?
All the examples I have seen have one controller running code after a certain promise resolves, but not multiple controllers listening for the same promise to resolve.
here is some code im borrowing from this article (ill add a 'mother controller' to illustrate my example, I dont want the son service to ever make his http call twice): http://andyshora.com/promises-angularjs-explained-as-cartoon.html
son service
app.factory('SonService', function ($http, $q) {
return {
getWeather: function() {
// the $http API is based on the deferred/promise APIs exposed by the $q service
// so it returns a promise for us by default
return $http.get('http://fishing-weather-api.com/sunday/afternoon')
.then(function(response) {
if (typeof response.data === 'object') {
return response.data;
} else {
// invalid response
return $q.reject(response.data);
}
}, function(response) {
// something went wrong
return $q.reject(response.data);
});
}
};
});
father Controller:
// function somewhere in father-controller.js
var makePromiseWithSon = function() {
// This service's function returns a promise, but we'll deal with that shortly
SonService.getWeather()
// then() called when son gets back
.then(function(data) {
// promise fulfilled
if (data.forecast==='good') {
prepareFishingTrip();
} else {
prepareSundayRoastDinner();
}
}, function(error) {
// promise rejected, could log the error with: console.log('error', error);
prepareSundayRoastDinner();
});
};
Mother Controller:
var makePromiseWithSon = function() {
SonService.getWeather()
// then() called when son gets back
.then(function(data) {
// promise fulfilled
if (data.forecast==='good') {
workInTheGarden();
} else {
sweepTheHouse();
}
}, function(error) {
// promise rejected, could log the error with: console.log('error', error);
sweepTheHouse();
});
};
To have your factory service only get the url once, store the httpPromise in your factory service.
app.factory('SonService', function ($http) {
var weatherPromise;
function getWeather() {
return $http.get('http://fishing-weather-api.com/sunday/afternoon')
.then(function(response) {
if (typeof response.data === 'object') {
return response.data;
} else {
// invalid response
throw response;
}
}, function(response) {
// something went wrong
throw response;
});
}
function sonService() {
if (!weatherPromise) {
//save the httpPromise
weatherPromise = getWeather();
}
return weatherPromise;
}
return sonService;
});
The simple answer, in a non-angular-specific (but really easy to apply to Angular) way, is to create a service which caches ON-OUTBOUND-REQUEST (rather than caching return values, like most systems would).
function SearchService (fetch) {
var cache = { };
return {
getSpecificThing: function (uri) {
var cachedSearch = cache[uri];
if (!cachedSearch) {
cachedSearch = fetch(uri).then(prepareData);
cache[uri] = cachedSearch;
}
return cachedSearch;
}
};
}
function A (searchService) {
var a = this;
Object.assign(a, {
load: function ( ) {
searchService.getSpecificThing("/abc").then(a.init.bind(a));
},
init: function (data) { /* ... */ }
});
}
function B (searchService) {
var b = this;
Object.assign(b, {
load: function ( ) {
searchService.getSpecificThing("/abc").then(b.init.bind(b));
},
init: function (data) { /* ... */ }
});
}
var searchService = SearchService(fetch);
var a = new A(searchService);
var b = new B(searchService);
a.load().then(/* is initialized */);
b.load().then(/* is initialized */);
They're sharing the same promise, because the service they were talking to cached and returned the same promise.
If you wanted to be safe, you could cache a promise and then return new instances of promises which resolve (or reject) based on the cached promise.
// instead of
return cachedSearch;
// replace it with
return Promise.resolve(cachedSearch);
Each user is now getting a new instance, every time you make a request, but each instance is also passing or failing based on the original cached call.
And of course you can take it further, and put a time-limit on the cache, or have hooks to invalidate the cache, or whatever...
Converting this to Angular is also a snap
SearchService is a service
A and B are controllers
use $http instead of fetch (though fetch is really pretty)
in fetch( ).then(prepareData) you'd be converting data from JSON on success;
in $http, you'd be returning response.data because your users don't want to have to do that
either way, you're performing that operation exactly once, per outbound call, so cache it, too
use $q (and q methods) instead of native Promise
use angular.extend, instead of Object.assign
You're done; you've now ported that whole concept into Angular AND VanillaJS
I am developing a file reading service that look like this:
angular.factory('fileService', fileService);
function fileService($cordovaFile){
var service = {
readFile: readFile
};
return service;
///////////////
function readFile(path, file){
$cordovaFile.readAsText(path, file)
.then(function (success) {
console.log("read file success");
console.log(success);
return success;
}, function (error) {
alert("Fail to read file:"+error);
console.log("Fail to read file");
console.log(error);
return false;
});
}
}
And then using it like this:
var data = fileService.readFile(cordova.file.dataDirectory,filename);
console.log(data) //return undefined
The problem is it fail to return the data. How can I get the data return back?
Your problem is that you are not actually returning any result from the readFile function. You are returning data from your callback functions but if you come to think of it...that result is returned to the function readFile itself and it stays inside that function. What you would want to do is return the whole result of the function readFile and then resolve the promise in the controller where you use it. Here is the code:
angular.factory('fileService', fileService);
function fileService($cordovaFile){
var service = {
readFile: readFile
};
return service;
function readFile(path, file){
return $cordovaFile.readAsText(path, file);
}
}
And then you use it like this:
var data = fileService.readFile(cordova.file.dataDirectory,filename);
data.then(function (success) {
// Do whatever you need to do with the result
}, function (error) {
/// Handle errors
});
In general, when you use services to implement some kind of functionality that uses promises and returns result, you should always return the promise object which can be than resolved anywhere that it is needed.
I highly recommend that you read this great explanation for promise objects.
Your function readFile returns nothing, so, firstly you should be returning the promise:
function readFile(path, file) {
return
$cordovaFile.readAsText(path, file).then(function (success) {
console.log('Read file success');
console.log(success);
return success;
}, function (error) {
alert('Fail to read file: ' + error);
console.log('Fail to read file');
console.log(error);
return false;
});
}
And then, if you try to use it the way you were, you'll not get undefined anymore, you'll get a promise.
But since it's an async method, you'll get that promise still pending, and you probably don't want that, since you'll need the promise's fulfilled value. So, you should use it like this:
fileService.readFile(cordova.file.dataDirectory, filename).then(function(data) {
// use data here
});
i have this code for a statistic report over a db.
exports.calculate = function(req, res, next) {
models.Quiz.count()
.then(function(questions) {
statistics.questions = questions;
models.Comment.count().then(function(comments) {
statistics.comments = comments;
statistics.average_comments = (statistics.comments / statistics.questions).toFixed(2);
models.Quiz.findAll({
include: [{model: models.Comment}]})
.then(function(quizes) {
for (index in quizes) {
if (quizes[index].Comment.length) {
statistics.commented_questions++;
} else {statistics.no_commented++;}
};
})
})
})
.catch(function(error) {next(error)})
.finally(function() {next()});
};
It works properly until the SQL statement, but never makes the loop for, so i never can get
statistics.commented_questions
or
statistics.no_commented
Thank's in advanced!
When chaining promises together they need to know when the previous promise is either rejected or fulfilled. In your current code, the initial promise never returns a value/promise but instead calls an async function. The code essentially looks like this to the JS engine:
exports.calculate = function(req, res, next) {
models.Quiz.count()
.then(function(questions) {
statistics.questions = questions;
// ASYNC FUNCS THAT ARE NEVER RETURNED
// ...
// functions in JS without an explicit return statement return nothing (essentially `undefined`)
})
.catch(function(error) {
next(error)
})
.finally(function() {
next()
});
};
So, after the engine waits for the initial promise to be fulfilled/rejected it fires off another promise for an async operation that returns a promise but doesn't return it to the original promise chain. By default the original promise chain receives undefined which is then passed on to the next method in the chain. In this case it would be the finally method.
You might wonder why the second promise is still updating the information if it's not waiting for it. This is a race condition and essentially that promise is winning.
To properly chain the promises together you need to return the new promise to the old promise chain like so:
exports.calculate = function(req, res, next) {
models.Quiz.count().then(function(questions) {
statistics.questions = questions;
return models.Comment.count();
}).then(function(comments) {
statistics.comments = comments;
statistics.average_comments = (statistics.comments / statistics.questions).toFixed(2);
return models.Quiz.findAll({
include: [{
model: models.Comment
}]
});
}).then(function(quizes) {
for (index in quizes) {
if (quizes[index].Comment.length) {
statistics.commented_questions++;
} else {
statistics.no_commented++;
}
}
}).catch(next).finally(next);
};
If you are using a version of Node/IO that has support for the native Promise object you can leverage that a bit to issue concurrent requests since none of them are dependent on each other. Note: the Promise API does not have a finally() method but we can use the second argument for then() to pass an error along.
exports.calculate = function(req, res, next) {
Promise.all([
models.Quiz.count(),
models.Comment.count(),
models.Quiz.findAll({
include: [{
model: models.Comment
}]
})
]).then(function(results)
// `results` is an array of [questions, comments, quizes]
statistics.questions = results[0];
statistics.comments = results[1];
statistics.average_comments = (statistics.comments / statistics.questions).toFixed(2);
for (index in results[2]) {
if (results[2][index].Comment.length) {
statistics.commented_questions++;
} else {
statistics.no_commented++;
}
}
}).then(next, next);
};