Trying to learn how to transform http responses using pipes. The following function is called outside of that service:
public requestProjectList(assetId: number) {
this.service.get(url)
.pipe(map(data => {
console.log(data);
}));
}
However I am not getting data.What am I doing wrong? Please explain.
Thanks
unlike promises, observables are "cold" until they're subscribed to, meaning the function and the transforms won't run until you subscribe.
public requestProjectList(assetId: number) {
// return the observable
return this.service.get(url)
.pipe(map(data => {
console.log(data);
return data; // also return inside map prefer `tap` for simple logging
}));
}
then call your function and subscribe to trigger:
this.service.requestProjectList(id).subscribe(result => console.log(result, "got it"))
Related
I want to wait for one function to finish before executing the function next to it.
I have one function called getData() in which http call occurs which returns an observable. The second function checkDuplicate() we have subscribed to that function getData() . and we have third function called proceed() in which we call the checkDuplicate() function and once the checkDuplicate() function is completed, we are calling alert("finished"). But the issue here is even before checkDuplicate() function is completed, alert has been triggered.
find the code for better clarification:
getData(): Observable<any>{
return this.http.get<any>(URL...);
}
checkDuplicate(){
return this.getData().subscribe(response => {
if(response){
console.log("Inside");
}
});
}
proceed(){
this.checkDuplicate();
console.log("finished");
}
Actual Result
finished
Inside
Expected result
Inside
finished
I have tried asyn/await to wait for checkDuplicate() function to finish. But still no use.
It would be grateful if u share the solution.
Thanks in Advance !!!
The gotcha of async programming. Here's a solution with promises (you can also use observables):
getData(): Observable<any>{
return this.http.get<any>(URL...);
}
async checkDuplicate(){
var response = await this.getData().toPromise();
if (response) {
console.log("Inside");
}
}
async proceed(){
await this.checkDuplicate();
console.log("finished");
}
You can update something like this.
getData(): Observable<any>{
return this.http.get<any>(URL...);
}
checkDuplicate() {
return Promise(resolve=>{
this.getData().subscribe(response => {
if(response){
console.log("Inside");
}
resolve(true);
});
});
}
proceed(){
this.checkDuplicate().then(value=>{
console.log("Return from promise => "+ value);
console.log("finished");
});
}
You can return promise from checkDuplicate and call another function after it is resolved by using "then".
function one_by_one(objects_array, iterator, callback) {
var start_promise = objects_array.reduce(function (prom, object) {
return prom.then(function () {
return iterator(object);
});
}, Promise.resolve()); // initial
if(callback){
start_promise.then(callback);
}else{
return start_promise;
}
}
one_by_one(requestBodyAll,task);
I tried the above code but it processed only the first file. Can someone help please?
requestBodyAll - contains the files list to process.
task - function returns promise.
Looks about right. If the reduction is not progressing past the first file, then it's likely that task(first_file) throws. You can find out by logging errors.
Also, you're better off returning a promise from one_by_one and not passing a callback.
function one_by_one(objects_array, work) {
return objects_array.reduce(function (prom, object) {
return prom.then(function() {
return work(object);
});
}, Promise.resolve()); // initial
}
one_by_one(requestBodyAll, someWorkFn)
.then(task)
.catch(function(error) {
console.log(error);
});
one_by_one() will (as in the question) deliver the result of iterator(last_file) as an argument to task. You may prefer to pass nothing, in which case write :
.then(function() {
return task();
})
You could immunize the process against errors by catching inside one_by_one().
I'm trying to create actions from updates from a RX Subject
It's working but I get the error below.
Here is my Epic
export function uploadSceneFile(action$, store) {
return action$.ofType(CREATE_SCENE_SUCCESS)
.mergeMap(({payload}) =>
UploadSceneWithFile(payload)
.subscribe(res => {
if (res.progress > 0)
store.dispatch(uploadSceneProgress(res))
else if(res.progress === -1){
store.dispatch(uploadSceneSuccess(res))
requestSceneProcessing(res).map(res => {
})
}
})
)
}
And here is the Subject
export function UploadSceneWithFile(scene){
const subject$ = new Subject()
const uploader = new S3Upload({
getSignedUrl: getSignedUrl,
uploadRequestHeaders: {'x-amz-acl': 'public-read'},
contentType: scene.file.type,
contentDisposition: 'auto',
s3path: 'assets/',
onError:()=>subject$.next('error'),
onProgress: (val)=> subject$.next({...scene,progress:val}),
onFinishS3Put: ()=>subject$.next({...scene,progress:-1}),
})
uploader.uploadFile(scene.file)
return subject$
}
I read from a previous post that I'm supposed to be using .map, not .subscribe but nothing happens if I don't subscribe (the upload doesn't happen)
What's the best way of doing this?
subscribeToResult.js:74 Uncaught TypeError: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
at Object.subscribeToResult (subscribeToResult.js:74)
at MergeMapSubscriber../node_modules/rxjs/operators/mergeMap.js.MergeMapSubscriber._innerSub (mergeMap.js:132)
at MergeMapSubscriber../node_modules/rxjs/operators/mergeMap.js.MergeMapSubscriber._tryNext (mergeMap.js:129)
at MergeMapSubscriber../node_modules/rxjs/operators/mergeMap.js.MergeMapSubscriber._next (mergeMap.js:112)
at MergeMapSubscriber../node_modules/rxjs/Subscriber.js.Subscriber.next (Subscriber.js:89)
at FilterSubscriber../node_modules/rxjs/operators/filter.js.FilterSubscriber._next (filter.js:89)
at FilterSubscriber../node_modules/rxjs/Subscriber.js.Subscriber.next (Subscriber.js:89)
at Subject../node_modules/rxjs/Subject.js.Subject.next (Subject.js:55)
at createEpicMiddleware.js:60
at createEpicMiddleware.js:59
at SafeSubscriber.dispatch [as _next] (applyMiddleware.js:35)
at
The problem is that you subscribe inside mergeMap and return a Subscription which is invalid. The callback needs to return only Observable, Promise, Array, or Iterable.
I'm not sure what exactly you need to do but if you need to perform some side-effects you can use do() operator instead of subscribing.
export function uploadSceneFile(action$, store) {
return action$.ofType(CREATE_SCENE_SUCCESS)
.mergeMap(({ payload }) => UploadSceneWithFile(payload)
.do(res => {
...
})
)
}
Or it looks like you could put do after mergeMap as well:
export function uploadSceneFile(action$, store) {
return action$.ofType(CREATE_SCENE_SUCCESS)
.mergeMap(({ payload }) => UploadSceneWithFile(payload))
.do(res => {
...
});
}
I have a function that returns a BehaviorSubject but when I try to use the data I get back from the function I need to use it once all the data is back, is there a way to know when the BehaviorSubject is done pulling all the data?
I tried using .finally but it never gets called. Here is the code I'm using.
getData() {
let guideList = '';
this.getChildren(event.node)
.subscribe(
function(data) {
console.log('here');
guideList = data.join(',');
},
function(err) {
console.log('error');
},
function() {
console.log('done');
console.log(guideList);
}
);
}
getChildren(node: TreeNode) {
const nodeIds$ = new BehaviorSubject([]);
//doForAll is a promise
node.doForAll((data) => {
nodeIds$.next(nodeIds$.getValue().concat(data.id));
});
return nodeIds$;
}
Attached is a screen shot of the console.log
Easiest way is to just collect all the data in the array and only call next once the data is all collected. Even better: don't use a subject at all. It is very rare that one ever needs to create a subject. Often people use Subjects when instead they should be using a more streamlined observable factory method or operator:
getChildren(node: TreeNode) {
return Observable.defer(() => {
const result = [];
return node.doForAll(d => result.push(d.id)).then(() => result);
});
}
I'm using the WebRTC library which has a very specific API. The peerConnection.setRemoteDescription method's 2nd argument is supposed to be a callback for when it finishes setting the remote description:
This is one of my wrapper functions for my WebRTC class:
export function setRemoteSdp(peerConnection, sdp, callback) {
if (!sdp) return;
return peerConnection.setRemoteDescription(
new RTCSessionDescription(sdp),
callback, // <-------------
);
}
And this is a sketch of what I want to do:
function receivedSdp(action$, store) {
return action$.ofType(VideoStream.RECEIVED_SDP)
.mergeMap(action => {
const {peerConnection} = store.getState().videoStreams;
const {sdp} = action.payload;
return WebRTC.setRemoteSdp(peerConnection, sdp, () => {
return myReducer.myAction(); // <------ return action as the callback
})
})
};
This doesn't work since I'm not returning an Observable. Is there a way to do this?
P.S. this is the WebRTC API: https://github.com/oney/react-native-webrtc/blob/master/RTCPeerConnection.js#L176
martin's answer is correct about using Observable.create or new Observable--same thing (except it's not clear to me why you need the mergeAll() since the mergeMap will flatten?)
As a bonus, you could also use Observable.bindCallback for this.
// bindCallback is a factory factory, it creates a function that
// when called with any arguments will return an Observable that
// wraps setRemoteSdp, handling the callback portion for you.
// I'm using setRemoteSdp.bind(WebRTC) because I don't know
// if setRemoteSdp requires its calling context to be WebRTC
// so it's "just in case". It might not be needed.
const setRemoteSdpObservable = Observable.bindCallback(WebRTC.setRemoteSdp.bind(WebRTC));
setRemoteSdpObservable(peerConnection, sdp)
.subscribe(d => console.log(d));
Usage inside your epic would be something like this
// observables are lazy, so defining this outside of our epic
// is totally cool--it only sets up the factory
const setRemoteSdpObservable = Observable.bindCallback(WebRTC.setRemoteSdp.bind(WebRTC));
function receivedSdp(action$, store) {
return action$.ofType(VideoStream.RECEIVED_SDP)
.mergeMap(action => {
const {peerConnection} = store.getState().videoStreams;
const {sdp} = action.payload;
return setRemoteSdpObservable(peerConnection)
.map(result => myReducer.myAction());
})
};
You could use this to create Observable wrappers for all the WebRTC apis.
So the problem is that setRemoteSdp doesn't return an Observable while myReducer.myAction() does and that's the Observable you want to merge?
You can use Observable.create and wrap the WebRTC.setRemoteSdp call:
.mergeMap(action => {
return Observable.create(observer => {
WebRTC.setRemoteSdp(peerConnection, sdp, () => {
observer.next(myReducer.myAction());
observer.complete();
})
});
}
.mergeAll()
The Observable.create returns an Observable that emits another Observable from myReducer.myAction(). Now I have in fact so-called higher-order that I want to flatten using mergeAll() (concatAll would work as well).