I want to add chaining to http.request(url).post(uri) from chai npm module for mocha test feramework.
so I need to add field and attach promises that is for post as long as the values from an array that will pass as parameters for these chai methods
as follow:
var props = ['name:Sara', 'email:sara#mail.com', 'password:pass'];
var route = '/app/login';
chaiHttp.request(url).post(route)
./* add field promises as much as the array length */
.then(function(res){
// do something with this res
});
I do something like this already:
var props = ['name:Sara', 'email:sara#mail.com', 'password:pass'];
var route = '/app/login';
chaiHttp.request(url).post(route).field('name','Sara').field('email','sara#mail.com').then(function(res){
//do something with response
});
The promise returned by post() should be the receiver of the then() call, but I think you're worried that the intervening field() calls will do some harm.
But the hard-coded version you have proves that the field calls propagate their receiver so they can be chained. Therefore, it's perfectly okay to do something like this:
var postPromise = chaiHttp.request(url).post(route); // this won't start until your code returns from the event loop
['name:Sara', 'email:sara#mail.com', 'password:pass'].forEach(function(pair) {
var components = pair.split(':');
postPromise.field(components[0], components[1]);
});
postPromise.then(function(res) {
});
Related
I'm creating a factory to take a userId from one page, make a call to a REST API, and return the results on the following view. My initial attempts were largely taken from this answer but - unsurprisingly - I keep getting caught in a situation where the doesn't respond in time and the get() method returns an empty array.
Here's the factory itself
app.factory('GetMessages', function() {
var messages = []
function set(userId) {
Restangular.all('/api/messages/').getList({'_id': userId}).then(function(docs){
messages = docs
})
}
function get() {
return messages;
}
return {
set: set,
get: get
}
});
For what it's worth I'm having no trouble getting the userId into the factory as it's just passed in on a function like this
view:
<a ng-click='passToFactory(message.user.id)' href='/home/inbox/reply'>Reply</a>
controller:
$scope.passToFactory = function(id) {
GetMessages.set(id);
};
and the controller for the following view is just
$scope.messages = GetMessages.get()
The issue I'm having is that after the factory returns the empty set no further changes from the factory are recognized (even though after time elapses it does get the proper response from the API) and $scope.messages remains empty.
I've attempted to move the API call to the get method (this hasn't worked as the get method often does not get the userId in time) and I can't find a way to use a promise to force get() to wait on set() completing.
I'd prefer to keep using Restangular in the eventual solution but this is a small thing that has taken too much time so any fix works.
I'm fairly new to Angular so I'm sure there's something totally obvious but right now I'm just lost. Thanks.
The race condition that you have is that the function inside the .then method is executed asynchronously after the call to the set function. If the get function executes before the $q service fulfills the promise, the get function returns an empty array.
The solution is to save the promise and chain from the promise.
app.factory('GetMessages', function() {
var promise;
function set(userId) {
promise = Restangular.all('/api/messages/').getList({'_id': userId});
}
function get() {
return promise;
}
return {
set: set,
get: get
}
});
In your controller, chain from the promise.
GetMessages.get.then( function (docs) {
$scope.messages = docs;
}) .catch ( function (error) {
//log error
};
For more information on chaining promises, see the AngularJS $q Service API Reference -- chaining promises.
You are breaking the reference to the original messages array when you reassign it.
Try:
Restangular.all('/api/messages/').getList({'_id': userId}).then(function(docs){
messages.concat(docs) ; // keep same array reference
});
Simple example to explain why it isn't working
var arr = [];
var x = arr;
arr = [1,2,3]; // is now a different array reference
console.log(x); // is still empty array. x !== arr now
cherlietfl is right.
The problem is that you break the reference to the messages array since you assign a new array to messages inside your get function. But concat is doing this as well.
Try this:
Restangular.all('/api/messages/').getList({'_id': userId}).then(function(docs){
messages.splice(0, messages.length); // clear the array
messages.push.apply(messages, docs); //add the new content
});
Try assigning you function to the scope. Then call that function in the model. Like so:
// controller
$scope.getMessages = GetMessages.get;
View:
<div ng-repeat="message in getMessages()"></div>
This way when the request call finishes and the digest cycle goes through the watchers again, the get function will be called and you will get your messages.
Is there a way to make sure the order on how subscribers get updated is ensured?
I've got a hot observable and my first subscriber does some sync work to update a variable and my next subscriber then has to initialise a service (only once!), and only after that variable is ensured to be set!
it looks like this:
import App from './App'
var appSource = App.init() // gets the hot observable
// our second subscriber
appSource.take(1).subscribe(() => {
// take 1 to only run this once
nextService.init()
})
where App.init looks like this:
...
init() {
var source = this.createObservable() // returns a hot interval observable that fetches a resource every few minutes
// first subscriber, updates the `myVar` every few minutes
source.subscribe((data) => this.myVar = data)
return source
}
...
this currently works, but I am unsure if it will always follow the order 100%.
EDIT:
As I've heard, subscribers will be invoked FIFO. So the order is somewhat assured.
I don't know if RxJS ever explicitly guarantees that observers are called in order of subscription. But, as you say, it usually works.
However, you might consider modelling your actual workflow instead of relying on implicit observer order.
It sounds like you need to know when your app is initialized so you can take further action. Instead of relying on knowledge of the internal workings of App.init, App could expose an API for this:
One (non-Rx way) is to let the caller supply a callback to init:
//...
init(callback) {
var source = this.createObservable() // returns a hot interval observable that fetches a resource every few minutes
// first subscriber, updates the `myVar` every few minutes
source.subscribe((data) => {
this.myVar = data;
if (callback) {
callback();
callback = undefined;
}
})
return source
}
// elsewhere
App.init(() => nextService.init());
Another option instead of a callback is to just have init return a Promise that your resolve (or an Rx.AsyncSubject that you signal) once initialization is complete.
And yet another option, but requires a bit of a refactor, is to model this.myVar as the observable data that it is. i.e.:
init() {
this.myVar = this.createObservable().replay(1);
this.myVar.connect();
// returns an observable that signals when we are initialized
return this.myVar.first();
}
// elsewhere, you end up with this pattern...
const servicesToInit = [ App, service1, service2, service3 ];
Observable
.of(servicesToInit)
.concatMap(s => Rx.Observable.defer(() => s.init()))
.toArray()
.subscribe(results => {
// all initializations complete
// results is an array containing the value returned by each service's init observable
});
Now, anything that wants to make use of myVar would always need to subscribe to it in someway to get the current and/or future values. They could never just synchronously ask for the current value.
Let's say you have code that returns an object containing pre-programmed functions, which you use like this:
someFunction(/* pass in data */)
.post(/* some data */) //Returned post function is pre-programmed to send a POST request to the right URL
.then(function() {
//do something
});
How can I unit test this using sinon.js?
To direct what a function returns, you need to use a stub:
var mockService = sinon.stub();
mockService.returns(/* some return value */);
But let's say I want to verify that someFunction was called with the right arguments, in addition to verifying that the returned post function was called with the right arguments. I would need a stub to return a stub:
mockService.returns({
post: sinon.stub()
});
How can I access mockService.post to verify that the right arguments were passed, in this case?
Bonus Question: What's the name of this design pattern (returning a function with pre-programmed behavior)? I've seen it used before, but don't know the name of it.
Since no one answered, I worked around this like this:
var mockServicePost = sinon.stub();
var mockService = sinon.stub();
mockService.returns({
post: mockServicePost
});
But I am interested to know if there's a more elegant solution than this.
I have an http request that should return a list of tasks. However, those tasks are generated in a complex fashion. This is how it works.
Get all current tasks from the DB
expire tasks that are old
get user profiles from the DB
if the user doesn't have a profile and a task for creating the profile doesn't exist, add a task for creating the profile
additionally, for every subprofile the user has, make a daily task and save it to the DB, if a daily task hasn't already been created.
return all the tasks to the HTTP caller
I'm listing this all here, in case there's a better way to do it. From what I understand, I should have promises for both the DB calls that are then followed by promises that manipulate the task/profile data.
What I don't understand is how to add the N promises that are needed for daily tasks into my promise chain. I also need all the data available the final process to return the newly created list of tasks. Should I be nesting promises somehow?
Currently, I imagine it being something like this:
var taskPromise = dbPromise(serverName, taskRequest, params);
var profilesPromise = dbPromise(serverName, profilesRequest, params);
Q.all([taskPromise, profilesPromise])
.then(function(arrayOfTasksAndProfiles){
//do calculations and create an object like {tasks:[], profile:profile, subprofiles:[]})
.then(function(currentDataObject) {
var deferred = Q.defer();
var newTasksToBeCreated = // make a list of all the new tasks I want to create
var promisesForNewTasks = [] // create an array of promises that save each of the new tasks to the server
Q.all(promisesForNewTasks)
.then(function(returnedIDsForNewTasks) {
// somehow match the returned IDs to the newTasksToBeCreated and add them on
currentDataObject.newTasks = newTasksToBeCreated
deferred.resolve(currentDataObject);
});)
.then(function(currentDataObject) {
// now that the currentDataObject has all the tasks from the DB, plus the new ones with their IDs, I can respond with that information
res.json(currentDataObject))
.done();
I have to make multiple calls to the DB to create new tasks, and I need to return those appended to the other tasks I received from the DB, and the only way I can see to do that is to nest a Q.all() call.
"There's gotta be a better way."
Only one thing: Don't create a custom deferred that you need to manually resolve. Instead, just return from the then handler; and return the resulting promise of the .then() call.
.then(function(currentDataObject) {
var newTasksToBeCreated = // make a list of all the new tasks I want to create
var promisesForNewTasks = [] // create an array of promises that save each of the new tasks to the server
return Q.all(promisesForNewTasks)
// ^^^^^^
.then(function(returnedIDsForNewTasks) {
// somehow match the returned IDs to the newTasksToBeCreated and add them on
currentDataObject.newTasks = newTasksToBeCreated
return currentDataObject;
// ^^^^^^
});
})
Else, it looks quite fine. If you have problems matching the returned ids to the tasks - don't do it that way. Instead, make each of the promisesForNewTasks resolve with its own task object (combined with the id?).
I'm using the JayData.js library. It works quite well. However, I have a few situations where I've got a toArray() call deep in the function tree. Rather than trying to access my "busy" signal from there, I'd just as soon have the method block. Is that possible? I'm picturing something like "context.Groups.toArray(myObservableVar).block()".
Update 1: It appears that the JayData library returns a jQuery deferred object judging from the use of "then" and "when" operators on the return value. Is there a corresponding method to "join" -- meaning wait for the finish?
Indeed JayData toArray() (and all relevant data returning or saving/updating method) implements jQuery deferred. As from 1.0.5 you have to include the JayDataModules/deferred.js in order to this functionality to work.
For your use case $.when might be an answer:
var customers = context.Customers.toArray();
var products = context.Products.toArray();
var suppliers = context.Suppliers.toArray();
$.when(customers, products, suppliers).then(function(customers, products, suppliers) {
//we have everything here
//notice the the parameter names are shadowed as the var customers variable only
//holds a promise not the value
customers.forEach( ... );
products[12].ProductName = "X";
});
A blockUntilDone() method would go against the principles of deferred execution and continuations. JayData's toArray() is asynchronous because it is designed not to block the caller.
If you want this kind of code:
// Initialize context and groups...
var arrayOfGroups = context.Groups.toArray(); // Want synchronous behavior.
// Do something with 'arrayOfGroups'...
Trying to block until the deferred is resolved is not the solution. Move the last part of your code into a callback passed to toArray() instead:
// Initialize context and groups...
context.Groups.toArray(function(arrayOfGroups) {
// Do something with 'arrayOfGroups'...
});
Alternatively, bind to the returned promise with done() or then():
context.Groups.toArray().done(function(arrayOfGroups) {
// Do something with 'arrayOfGroups'...
});