how to EventSource with Redux Observable - javascript

The question is simple, how can i use redux-observable with an EventSource?
With RxJs its like:
const observable = Observable.create(observer => {
const eventSource = new EventSource('/model-observable');
return () => {
eventSource.close();
};
});
observable.subscribe({
next: data => {
this.zone.run(() => this.someStrings.push(data));
},
error: err => console.error('something wrong occurred: ' + err)
});

This sounds more like a general RxJS question about how to connect to an EventSource. This can be done in a number of ways. If all you care about are the messages (and not errors/open):
import { fromEvent } from 'rxjs/observable/fromEvent';
const fromEventSource = url => {
return new Observable(observer => {
const source = new EventSource(url);
const message$ = fromEvent(source, 'message');
const subscription = message$.subscribe(observer);
return () => {
subscription.unsubscribe();
source.close();
};
});
};
If you care about open and/or errors, it requires a little more code to pipe everything together:
import { Observable } from 'rxjs/Observable';
import { Subscriber } from 'rxjs/Subscriber';
const fromEventSource = (url, openObserver) => {
return new Observable(observer => {
const open = new Subscriber(openObserver);
const source = new EventSource(url);
const onOpen = event => {
open.next(event);
open.complete();
};
const onError = event => {
if (event.readyState === EventSource.CLOSED) {
observer.complete();
} else {
observer.error(event);
}
};
const onMessage = event => {
observer.next(event.data);
};
source.addEventListener('open', onOpen, false);
source.addEventListener('error', onError, false);
source.addEventListener('message', onMessage, false);
return () => {
source.removeEventListener('open', onOpen, false);
source.removeEventListener('error', onError, false);
source.removeEventListener('message', onMessage, false);
source.close();
};
});
};
fromEventSource('http://some-url.com')
.subscribe(value => console.log(value));
Usage in redux-observable would be something like this:
const somethingEpic = action$ =>
action$.ofType(SOMETHING)
.mergeMap(() =>
fromEventSource('http://some-url.com')
.map(message => ({
type: MESSAGE,
payload: message
}))
.catch(e => Observable.of({
type: SOMETHING_ERROR,
payload: e,
error: true
}))
);

Related

postMessage to iframe with a return MessageChannel using async await

I cant get async-await to work when using postMessage and a MessageChannel
const iframe = document.querySelector("iframe");
const sendMsgOnPort = async (msg) => {
const channel = new MessageChannel();
const testfunc = async () => {
channel.port1.onmessage = ({ data }) => {
channel.port1.close();
if (data.error) {
return data.error;
} else {
return data.result;
}
};
};
iframe.contentWindow.postMessage(`${msg}`, "*", [channel.port2]);
await testfunc();
};
iframe.addEventListener(
"load",
async () => {
console.log(await sendMsgOnPort("msg"));
},
true
);
and the child I have
window.addEventListener("message", (event) => {
try {
event.ports[0].postMessage({ result: `${event.data} back` });
} catch (e) {
event.ports[0].postMessage({ error: e });
}
});
I can get it to work with
const sendMsgOnPort = (msg) =>
new Promise((res, rej) => {
const channel = new MessageChannel();
channel.port1.onmessage = ({ data }) => {
channel.port1.close();
if (data.error) {
rej(data.error);
} else {
res(data.result);
}
};
iframe.contentWindow.postMessage(`${msg}`, "*", [channel.port2]);
});
but is there a way to do this without the new Promise((res, rej) => {
No.
You can only (usefully) await a promise.
You need new Promise to create one if you don't have one already (e.g. if the underlying API you are using returns a promise).
You don't have one already (not least because onmessage is a callback API designed to handle š¯‘› messages, not 1 message).

Function created using "crypto" won't return the bearerToken I created with it

I am sure this is something basic that I am missing but it's late and my brain gets like that. So I have this function:
export const createRandomHex = () => {
try {
return crypto.randomBytes(127, (_err, buf) => {
console.log("[create] bearerToken: ", buf.toString("hex"));
const bearerTokenString = buf.toString("hex");
return bearerTokenString;
});
} catch (e) {
return e;
}
};
And I am calling it in another function as such:
export const createBearerToken = () => {
const bearerToken = createRandomHex();
}
For some reason, the value of bearerToken in the createBearerToken is undefined or null. Can anyone help me with this?
randomBytes is asynchronous and takes a callback. You could return a promise in your function:
export const createRandomHex = async () => {
return new Promise((resolve, reject) => {
try {
return crypto.randomBytes(127, (_err, buf) => {
console.log("[create] bearerToken: ", buf.toString("hex"));
const bearerTokenString = buf.toString("hex");
resolve(bearerTokenString);
});
} catch (e) {
reject(e);
}
});
};

rxjs - operator with both on-next and on-error callbacks?

I'm looking for the equivalent of promise.then(onNextCallback,onErrorCallback) in rxjs.
is there something like this?
pipe(concatMap(),catchError) alternatives is not what I'm looking for.
I also asked for this as well and ended up writing my own.
import * as RxJS from 'rxjs';
/**
* Like `promise.then(onFulfilled, onRejected)`. This is *not* the same as
* `map(onNext).catchError(onError)`, because this operator will only catch errors resulting from
* the original observableā€”not errors resulting from `onNext`.
*/
export const mapOrCatchError = <T, B>(
onNext: (value: T) => B,
onError: (value: unknown) => B,
): RxJS.OperatorFunction<T, B> => ob$ =>
new RxJS.Observable<B>(observer =>
ob$.subscribe({
next: t => {
let next: B;
try {
next = onNext(t);
} catch (error) {
observer.error(error);
return;
}
observer.next(next);
},
error: error => {
let next: B;
try {
next = onError(error);
} catch (newError) {
observer.error(newError);
return;
}
observer.next(next);
observer.complete();
},
complete: () => {
observer.complete();
},
}),
);
Tests:
import { marbles } from 'rxjs-marbles/jest';
import { mapOrCatchError } from '../operators';
describe('mapOrCatchError', () => {
it(
'should map',
marbles(m => {
const source$ = m.cold('--(a|)', { a: 1 });
const expected = ' --(b|)';
const actual$ = source$.pipe(
mapOrCatchError(
a => a + 1,
_error => 0,
),
);
m.expect(actual$).toBeObservable(expected, { b: 2 });
}),
);
it(
'should catch',
marbles(m => {
const source$ = m.cold('--#');
const expected = ' --(a|)';
const actual$ = source$.pipe(
mapOrCatchError(
a => a + 1,
_error => 0,
),
);
m.expect(actual$).toBeObservable(expected, { a: 0 });
}),
);
it(
'should error if error handler throws',
marbles(m => {
const source$ = m.cold('--#');
const expected = ' --#';
const error = new Error('foo');
const actual$ = source$.pipe(
mapOrCatchError(
a => a + 1,
_error => {
throw error;
},
),
);
m.expect(actual$).toBeObservable(expected, undefined, error);
}),
);
it(
'should not catch errors thrown by map function',
marbles(m => {
const source$ = m.cold('--(a|)');
const expected = ' --#';
const error = new Error('foo');
const actual$ = source$.pipe(
mapOrCatchError(
() => {
throw error;
},
_error => 'caught error',
),
);
m.expect(actual$).toBeObservable(expected, undefined, error);
}),
);
});
Indeed it is not of my knowledge any operator that would do what you need.
What I do suggest, though, it's to create your own.
function promiseLike<T>(
onNext: (data: T) => Observable<T>,
onError: (err: any) => Observable<any>
) {
type ErrorWrapper = {isError: boolean, err: any};
const isErrorWrapper = (err: any): err is ErrorWrapper => {
return err.isError && err.err !== undefined;
}
return function(source: Observable<T>): Observable<T> {
return source.pipe(
catchError((err) => of({isError: true, err})),
switchMap((data) => isErrorWrapper(data) ? onError(data.err) : onNext(data))
);
}
}
The above basically wraps the error from source observable and then we switchMap to decide if I run the onNext and onError. This way, for sure, the onError won't catch possible errors coming from the onNext.
Here are the example of usage:
function getResolvingPromise(): Promise<string> {
return Promise.resolve('SUCCESS');
}
function getErrorPromise(): Promise<string> {
return Promise.reject('onError');
}
// Example with success
from(getResolvingPromise()).pipe(
promiseLike(
(data) => of(`received ${data}`),
(err) => of(3)
)
).subscribe((d) => console.log('Ex1', d)) // Logs Ex1 received SUCCESS
// Example with error
from(getErrorPromise()).pipe(
promiseLike(
(data) => of(`received ${data}`),
(err) => of(3)
)
).subscribe((d) => console.log('Ex2', d)) // Logs Ex2 3
// Example with internal error 2
from(getResolvingPromise()).pipe(
promiseLike(
(data) => throwError('Valid token not returned'),
(err) => of(3)
),
catchError(() => of('catching internal error'))
).subscribe((d) => console.log('Ex3', d)) // Logs Ex3 catching internal error
What about something like this?
source$ is the source Observable and you can provide corresponding functions via tap operator inside the pipe.
source$.pipe(
tap({ next: onNextCallback, error: onErrorCallback })
)
Is .toPromise() what are you looking for ?
https://www.learnrxjs.io/learn-rxjs/operators/utility/topromise
Then you can do your .then(nextCallback) .catch(errorCallback)

redux-form: How to throw new SubmissionError in Promise?

I got two variants of code - want to throw SubmissionError (redux-form), in first code it works fine, and I got "Uncaught (in promise)" in second. Tried to fix second code. Can any one tell why the second code cant throw SubmissionError? Tried do it with reject, but its not work too. Is it possible to throw SubmissionError with new Promise?
handleSubmit = async (values) => {
const { addItem, reset } = this.props;
await addItem({ item: values }).catch ((e) => {
throw new SubmissionError({ _error: e.message });
});
}
second not works:
handleSubmit = (values) => {
const { addItem, reset } = this.props;
const promise = (item) => new Promise((resolve) => {
addItem(item);
resolve();
});
return promise(values).catch((e) => {
throw new SubmissionError({ _error: e.message });
});
}
"addItem" function (if its needed):
export const addItem = ({ text }) => async (dispatch) => {
try {
const url = routes.Url();
const response = await axios.post(url, { item: { text } });
dispatch(addItemSuccess({ item: response.data }));
} catch (e) {
throw e;
}
};
hmm, I think the problem in that you don't await your the promise. You return promise instead of the promise result, as result your code is not sync and SubmissionError lost context
Try it:
handleSubmit = (values) => {
const { addItem, reset } = this.props;
const promise = (item) => new Promise((resolve) => {
addItem(item);
resolve();
});
return await promise(values).catch((e) => {
throw new SubmissionError({ _error: e.message });
});
}
Exists alternative way to set async errors to the form via stopSubmit
handleSubmit = (values) => {
const { addItem, reset } = this.props;
const promise = (item) => new Promise((resolve) => {
addItem(item);
resolve();
});
return promise(values).catch((e) => {
dispatch(stopSubmit('FORM_NAME', { _error: e.message }));
});
}

react.js: Create resource with redux-form, rest api and async/await

I'm trying to create new resource with redux form and REST api.
I dispatch createPost action and I want to check if the post was succeeded before continue.
const handleFormSubmit = (values, dispatch) => {
dispatch(createPost(values));
//I want to check here if post was succeeded.
//if status = 200 this.props.history.push('/');
}
export function createPost(values) {
return async function(dispatch) {
let request;
try {
request = await axios.post(`${ROOT_URL}/posts`, values)
} catch(err) {
request = { err };
}
dispatch({
type: CREATE_POST,
payload: request
})
}
}
Return a promise, something like this :
export function createPost(values) {
return function(dispatch) {
return new Promise( async function(resolve, reject){
let request;
try {
request = await axios.post(`${ROOT_URL}/posts`, values)
} catch(err) {
reject(err)
}
dispatch({
type: CREATE_POST,
payload: request
})
resolve(request)
})
}
}
const handleFormSubmit = () => {
dispatch(createPost(values))
.then( res => {
// do yoour stuff on succes
} )
.catch(err => {
// stuff on err
})
}
As seeing your codes, I don't think you need to use promise.
Please try like following:
const getAction = (values) => (dispatch) => {
return axios
.post(`${ROOT_URL}/posts`, values)
.then(
() => {
dispatch({
type: CREATE_POST,
payload: request
})
},
() => {
throw new SubmissionError({
_error: 'Failed'
});
}
);
};
const handleSubmit = (values) => {
return dispatch(getAction(values));
};

Categories