Using RxJS How can I pass new properties on the observer? So basically I want the prop "customProp" to be available to observable
const { Observable } = require('rxjs');
const observable = Observable.create(function (observer) {
console.log(observer.customProp); //How to get this working?
observer.next(1);
observer.next(2);
observer.next(3);
setTimeout(() => {
observer.next(4);
observer.complete();
}, 1000);
});
console.log('just before subscribe');
observable.subscribe({
next: x => console.log('got value ' + x),
error: err => console.error('something wrong occurred: ' + err),
complete: () => console.log('done'),
customProp: 'Hello World RxJS',
});
console.log('just after subscribe');
---->>> Output
just before subscribe
undefined //How can I get this working, please?
got value 1
got value 2
got value 3
just after subscribe
=> undefined
got value 4
done
Adding more info -- so basically I am trying to create a cold observable in which the producer needs a prop that should come from the subscriber
//Cold observable
var coldObservable = new Observable((observer) => {
var myProducerObj = new MyProducer();
myProducerObj.executeQuery(observer.customProp);
// How could we get customProp over here?
});
Adding usage info ---
coldObservable is DB connection function and customProp is the query that needs to be executed on the DB
This is not possible without extending Observable class and this is not needed. It could be factory function:
const createObservableWithCustomProp = (customProp, observer) => {
return new Observable((observer) => {
var myProducerObj = new MyProducer(customProp);
...
});
};
Usually there is rarely a need to construct observables manually because RxJS API provides rich feature set to create, transform and combine observables.
Related
My code:
var price = {};
function getPrice(price) {
const https = require('https');
var item = ('M4A1-S | Decimator (Field-Tested)')
var body = '';
var price = {};
https.get('https://steamcommunity.com/market/priceoverview/?appid=730&market_hash_name=' + item, res => {
res.on('data', data => {
body += data;
})
res.on('end', () => price ['value'] = parseFloat(JSON.parse(body).median_price.substr(1))); //doesnt add to dict
}).on('error', error => console.error(error.message));
}
price['test'] = "123" //adds to dict fine
getPrice(price)
console.log(price);
Output:
{ test: '123' }
as you can see, the "test: 123" gets added, but the "value: xxx" from the function doesn't. Why is that?
There are two main problems here:
You're redeclaring the variable inside your function so you're declaring a separate, new variable and modifying that so the higher scoped variable, never gets your .value property.
You're assigning the property inside an asynchronous callback that runs sometime later after your function has returned and thus your function actually returns and you do the console.log() too soon before you have even obtained the value. This is a classic issue with return asynchronously obtained data from a function in Javascript. You will need to communicate back that data with a callback or with a promise.
I would also suggest that you use a higher level library that supports promises for getting your http request and parsing the results. There are many that already support promises, already read the whole response, already offer JSON parsing built-in, do appropriate error detection and propagation, etc... You don't need to write all that yourself. My favorite library for this is got(), but you can see a list of many good choices here. I would strongly advise that you use promises to communicate back your asynchronous result.
My suggestion for fixing this would be this code:
const got = require('got');
async function getPrice() {
const item = 'M4A1-S | Decimator (Field-Tested)';
const url = 'https://steamcommunity.com/market/priceoverview/?appid=730&market_hash_name=' + item;
const body = await got(url).json();
if (!body.success || !body.median_price) {
throw new Error('Could not obtain price');
}
return parseFloat(body.median_price.substr(1));
}
getPrice().then(value => {
// use value here
console.log(value);
}).catch(err => {
console.log(err);
});
When I run this, it logs 5.2.
You're actually console.logging .price before you're setting .value; .value isn't set until the asynchronous call fires.
You are declaring price again inside the function and also not waiting for the asynchronous task to finish.
const https = require("https");
const getPrice = () =>
new Promise((resolve, reject) => {
const item = "M4A1-S | Decimator (Field-Tested)";
let body = "";
return https
.get(
`https://steamcommunity.com/market/priceoverview/?appid=730&market_hash_name=${item}`,
res => {
res.on("data", data => {
body += data;
});
res.on("end", () =>
resolve(
parseFloat(JSON.parse(body).median_price.substr(1))
)
);
}
)
.on("error", error => reject(error));
});
const main = async () => {
try{
const price = await getPrice();
//use the price value to do something
}catch(error){
console.error(error);
}
};
main();
I am trying to rewrite a module I wrote that seeds a MongoDB database. It was originally working fine with callbacks, but I want to move to Promises. However, the execution and results don't seem to make any sense.
There are three general functions in a Seeder object:
// functions will be renamed
Seeder.prototype.connectPromise = function (url, opts) {
return new Promise((resolve,reject) => {
try {
mongoose.connect(url, opts).then(() => {
const connected = mongoose.connection.readyState == 1
this.connected = connected
resolve(connected)
})
} catch (error) {
reject(error)
}
})
}
[...]
Seeder.prototype.seedDataPromise = function (data) {
return new Promise((resolve,reject) => {
if (!this.connected) reject(new Error('Not connected to MongoDB'))
// Stores all promises to be resolved
var promises = []
// Fetch the model via its name string from mongoose
const Model = mongoose.model(data.model)
// For each object in the 'documents' field of the main object
data.documents.forEach((item) => {
// generates a Promise for a single item insertion.
promises.push(promise(Model, item))
})
// Fulfil each Promise in parallel
Promise.all(promises).then(resolve(true)).catch((e)=>{
reject(e)
})
})
}
[...]
Seeder.prototype.disconnect = function () {
mongoose.disconnect()
this.connected = false
this.listeners.forEach((l) => {
if (l.cause == 'onDisconnect') l.effect()
})
}
There is no issue with the main logic of the code. I can get it to seed the data correctly. However, when using Promises, the database is disconnected before anything else is every done, despite the disconnect function being called .finally().
I am running these functions like this:
Seeder.addListener('onConnect', function onConnect () { console.log('Connected') })
Seeder.addListener('onDisconnect', function onDisconnect () {console.log('Disconnected')})
Seeder.connectPromise(mongoURI, options).then(
Seeder.seedDataPromise(data)
).catch((error) => { <-- I am catching the error, why is it saying its unhandled?
console.error(error)
}).finally(Seeder.disconnect())
The output is this:
Disconnected
(node:14688) UnhandledPromiseRejectionWarning: Error: Not connected to MongoDB
at Promise (C:\Users\johnn\Documents\Code\node projects\mongoose-seeder\seed.js:83:37)
which frankly doesn't make sense to me, as on the line pointed out in the stack trace I call reject(). And this rejection is handled, because I have a catch statement as shown above. Further, I can't understand why the database never even has a chance to connect, given the finally() block should be called last.
The solution was to return the Promise.all call, in addition to other suggestions.
You are passing the wrong argument to then and finally. First here:
Seeder.connectPromise(mongoURI, options).then(
Seeder.seedDataPromise(data)
)
Instead of passing a callback function to then, you actually execute the function on the spot (so without waiting for the promise to resolve and trigger the then callback -- which is not a callback).
You should do:
Seeder.connectPromise(mongoURI, options).then(
() => Seeder.seedDataPromise(data)
)
A similar error is made here:
finally(Seeder.disconnect())
It should be:
finally(() => Seeder.disconnect())
Promise Constructor Anti-Pattern
Not related to your question, but you are implementing an antipattern, by creating new promises with new Promise, when in fact you already get promises from using the mongodb API.
For instance, you do this here:
Seeder.prototype.connectPromise = function (url, opts) {
return new Promise((resolve,reject) => {
try {
mongoose.connect(url, opts).then(() => {
const connected = mongoose.connection.readyState == 1
this.connected = connected
resolve(connected)
})
} catch (error) {
reject(error)
}
})
}
But the wrapping promise, created with new is just a wrapper that adds nothing useful. Just write:
Seeder.prototype.connectPromise = function (url, opts) {
return mongoose.connect(url, opts).then(() => {
const connected = mongoose.connection.readyState == 1
this.connected = connected
return connected;
});
}
The same happens in your next prototype function. I'll leave it to you to apply a similar simplification there, so avoiding the promise constructor antipattern.
In the later edit to your question, you included this change, but you did not return a promise in that function. Add return here:
return Promise.all(promises).then(() => {
//^^^^^^
return true
}).catch(() => {
console.log(`Connected:\t${this.connected}`)
})
I've been using firebase functions test to do some testing on my functions. I have some code that is supposed to post a thing to firestore, basically in the same way that the examples show to do in the realtime database examples:
exports.addMessage = functions.https.onRequest((req, res) => {
const original = req.query.text;
admin.firestore()
.collection('messages')
.add({ original })
.then(documentReference => res.send(documentReference))
.catch(error => res.send(error));
});
For my test, I've spoofed some basic functionality using sinon, mocha and chai. Here is my current test, which is failing with the error message: TypeError: firestoreService.snapshot_ is not a function
describe('addMessage', () => {
// add message should add a message to the database
let oldDatabase;
before(() => {
// Save the old database method so it can be restored after the test.
oldDatabase = admin.firestore;
});
after(() => {
// Restoring admin.database() to the original method.
admin.firestore = oldDatabase;
});
it('should return the correct data', (done) => {
// create stubs
const refStub = sinon.stub();
// create a fake request object
const req = {
query : {
text: 'fly you fools!'
}
};
const snap = test.firestore.makeDocumentSnapshot({ original: req.query.text }, 'messages/1234');
// create a fake document reference
const fakeDocRef = snap._ref;
// create a fake response object
const res = {
send: returnedDocRef => {
// test the result
assert.equal(returnedDocRef, fakeDocRef);
done();
}
};
// spoof firestore
const adminStub = sinon.stub(admin, 'firestore').get(() => () => {
return {
collection: () => {
return {
add: (data) => {
const secondSnap = test.firestore.makeDocumentSnapshot(data, 'messages/1234');
const anotherFakeDocRef = secondSnap._ref;
return Promise.resolve(anotherFakeDocRef);
}
}
}
}
});
// call the function to execute the test above
myFunctions.addMessage(req, res);
});
});
My question is how the heck do I fix this?
I previously had a test that was just passing the first snap and fakeDocRef, and my test was passing fine, but as soon as I resolve the promise with the new fake document reference, it fails...
Any help would be appreciated! Thanks!
There are three different types of the calls, that are different:
Operating on the Collections.
Operating on the Documents.
Operating on the results of the query.
They have to be used consistently.
Please refer a documentation to see the difference operation on the collection and the document.
My application uses an internal webservice for fetching data, i have a job which creates approx 500 requests which getsfired async to complete the fetch operation.
I make use of Axios, by creating an array of axios promises and then resolving them using using Axios.all();
It works fine until some 200 requests but post that i get socket hung up, however on the server side i see the requests are being processed.
How to configure axios to set custom time out, or is it a better idea to splice my promises array and then run them as multiple batches ?
Source code
let getAxiosPromiseArray = (urlList) => {
var axiosArrayofPromise = [];
return new Promise ( (resolve, reject) => {
try {
urlList.forEach ( (URL) => {
axiosArrayofPromise.push(axios.get(URL));
});
resolve(axiosArrayofPromise);
}
catch (err) {
reject("There is a problem getting Axios array of promises " + err);
}
})
}
async function processAxiosPromises (PromiseArray) {
try {
var results = []
results = await axios.all(PromiseArray);
return results;
}
catch(err) {
throw("There was a problem resolving promises array (Axios) " + err);
}
}
getallID().then ( (urlList) => {
return getAxiosPromiseArray(urlList);
}).then( (AxiosPromises) => {
return processAxiosPromises(AxiosPromises);
}).then ((resultData) => {
console.log(resultData);
});
Error
There was a problem resolving promises array (Axios) Error: socket hang up
First, that pair of functions getAxiosPromiseArray() and processAxiosPromises() needs fixing.
Your new Promise() construction is unnecessary. You can simply return Promise.all(arrayofPromise) (or axios.all(...) if you must) and do away with the other function.
Renaming the remaining function to something meaningful, you would end up with eg :
let getData = (urlList) => {
return Promise.all(urlList.map(URL => axios.get(URL)))
.catch(error => {
error.message = "There is a problem getting Axios array of promises " + error.message; // augment the error message ...
throw error; // ... and re-throw the errror.
});
};
And call as follows :
getallID().then(getData)
.then(resultData => {
console.log(resultData);
}).catch(error => {
console.error(error);
});
That will put you on solid ground but, on its own, is unlikely to fix a concurrency problem (if that's what it is), for which the simplest approach is to use Bluebird's Promise.map with the concurrency option.
The caller code can remain the same, just change getData(), as follows:
let getData = (urlList) => {
let concurrency = 10; // play with this value to find a reliable concurrency limit
return Promise.map(urlList, URL => axios.get(URL), {'concurrency': concurrency})
.catch(error => {
error.message = "There is a problem getting Axios array of promises " + error.message;
throw error;
});
};
// where `Promise` is Bluebird.
const axios = require('axios');
const axiosThrottle = require('axios-throttle');
//pass axios object and value of the delay between requests in ms
axiosThrottle.init(axios,200)
const options = {
method: 'GET',
};
const urlList = [
'https://jsonplaceholder.typicode.com/todos/1',
'https://jsonplaceholder.typicode.com/todos/2',
'https://jsonplaceholder.typicode.com/todos/3',
'https://jsonplaceholder.typicode.com/todos/4',
'https://jsonplaceholder.typicode.com/todos/5',
'https://jsonplaceholder.typicode.com/todos/6',
'https://jsonplaceholder.typicode.com/todos/7',
'https://jsonplaceholder.typicode.com/todos/8',
'https://jsonplaceholder.typicode.com/todos/9',
'https://jsonplaceholder.typicode.com/todos/10'
];
const promises = [];
const responseInterceptor = response => {
console.log(response.data);
return response;
};
//add interceptor to work with each response seperately when it is resolved
axios.interceptors.response.use(responseInterceptor, error => {
return Promise.reject(error);
});
for (let index = 0; index < urlList.length; index++) {
options.url = urlList[index];
promises.push(axiosThrottle.getRequestPromise(options, index));
}
//run when all promises are resolved
axios.all(promises).then(responses => {
console.log(responses.length);
});
https://github.com/arekgotfryd/axios-throttle
Hi I am pretty new to Angular and Observables
I am trying to GET Objects by theirs ID through a loop.
But don't receive my Response in Order.
Example
get ID(1)
get ID(2)
get ID(3)
Receive Object ID(2)
Receive Object ID(3)
Receive Object ID(1)
Is it possible to get my Objects back in order ??
Below is where I call multiple times my service function :
conferences-attendance.component.ts
ExportExcelAttendance() {
for (var i = 0; i < this.contactsAttendance.length; i++) {
this.practiceService.GetPracticebyDBID(this.contactsAttendance[i].practiceId)
.subscribe(
(practice: Practice) => {
this.practicesAttendance.push(practice);
if (this.practicesAttendance.length == this.contactsAttendance.length) {
this.ExportExcelAttendance2();
}
},
error => this.errorMessage = <any>error
);
}
}
Here is my function in my service, it where I receive the data (not in order with the calls).
practices.service.ts
GetPracticebyDBID(id: string) {
let params: URLSearchParams = new URLSearchParams();
params.set('thisId', id);
let requestOptions = new RequestOptions();
requestOptions.params = params;
return this.http.get('http://ec2-34-231-196-71.compute-1.amazonaws.com/getpractice', requestOptions)
.map((response: Response) => {
return response.json().obj;
})
.catch((error: Response) => Observable.throw(error.json()));
}
forkJoin gives you a little less code,
const arrayOfFetches = this.contactsAttendance
.map(attendee => this.practiceService.GetPracticebyDBID(attendee.practiceId) );
Observable.forkJoin(...arrayOfFetches)
.subscribe((practices: Practice[]) => {
this.practicesAttendance = practices;
this.ExportExcelAttendance2();
});
Edit
Snap! #Anas beat me to it. Although, I don't think you need the concatAll()
you should use concatAll operator to ensure calling your observables in sequence.
also, you can use completed callback to call ExportExcelAttendance2 instead of checking practicesAttendance length on every response callback.
check the below example:
let contactsAttendanceObservables = this.contactsAttendance
.map((item) => {
return this.practiceService.GetPracticebyDBID(item.practiceId);
});
Observable.of(...contactsAttendanceObservables)
.concatAll()
.subscribe(
(practice: Practice) => {
this.practicesAttendance.push(practice);
},
(err) => {
// handle any errors.
},
() => {
// completed
this.ExportExcelAttendance2();
}
);
if you still want your observables to run in parallel, you can use forkJoin Operator, which will emit the last value of all the passed observables to a one subscriber when all observables are completed.
check the below example:
let contactsAttendanceObservables = this.contactsAttendance
.map((item) => {
return this.practiceService.GetPracticebyDBID(item.practiceId);
});
Observable.forkJoin(...contactsAttendanceObservables)
.subscribe(
(practices: Practice[]) => {
this.practicesAttendance = practices;
this.ExportExcelAttendance2();
}
);
The forkJoin operator is simple to use. It waits until all observables complete, then emit an array with all the items emitted.
ExportExcelAttendance() {
const all = this.contactsAttendance.map(it => this.practiceService.GetPracticebyDBID(it.practiceId));
Rx.Observable.forkJoin(all)
.subscribe(
practicesAttendance => this.ExportExcelAttendance2(practicesAttendance),
error => this.errorMessage = < any > error);
}