Create an array from sync and async values - javascript

The code below works just fine. Console outputs an array [1, 2].
const getAsyncValue = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve(1);
}, 1000)
})
}
const getSyncValue = () => {
return Rx.Observable.of(2);
}
const observer = (arrayOfValues) => {
console.log(arrayOfValues);
}
Rx.Observable.of(getPromise(), getSyncValue())
.concatAll()
.toArray()
.subscribe(observer)
I'd like to change function getSyncFunction to the following (because in a real world application this function might not have a reference to RxJs library):
const getSyncValue = () => {
return 2;
}
If I just do that without anything else I get an error:
You provided '2' where a stream was expected
What other changes to the code do I need? Maybe hint me the operator to use.

The problem is not in getSyncValue() but in concatAll() that works with higher-order Observables. When you pass it just 2 it throws the error. Using Rx.Observable.of(2) is correct because it's an Observable that emits a single value 2 which is remitted by concatAll().
I don't know what your code should do but you could do for example:
Rx.Observable.of(getPromise(), getSyncValue())
.map(v => typeof(v) == 'object' ? v : Rx.Observable.of(v))
.concatAll()
...
However, I do recommend to rethink this because I guess you could use some easier approach than this.

Related

how to conditionally call an api depending on received value

i need to call an api depending on the value that im receiving on state,
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
const [selectedValue, setSelectedValue] = useState([])
const firstAPI = async (val) => {
return await axios
.get(`http://localhost:3000/api/v1/rendition?name=${val}&&excel=true`)
.then(res => console.log('excel', res))
}
const secondAPI = async (val) => {
const limit = pageSize * (page - 1);
return await axios
.get(`http://localhost:3000/api/v1/rendition?id=${val}&&skip=${limit}&&take=${pageSize}&&excel=true`)
.then((res) => {
console.log('excelID', res);
})
}
const handleExcelClick = (param) => {
if(param.filter(item => typeof item === 'string')){
firstAPI(param)
} else {
secondAPI(param)
}
}
<Button onClick={() => handleExcelClick(selectedValue)} > Excel </Button>
so im receveing a value from 2 different inputs, which are stored in selectedValue state, which can either be a string or a number, so i need to call an api depending on the value, if its a string, call the firstAPI, if it's a number, call the secondAPI. But whatever condition i put in, it ignores the if statement and just calls the first api which also triggers a 500 error because one of the apis only takes numbers as params, i don't know other way to approach this, any help will be much appreciated
Try with:
...
if(param.filter(item => isNaN(item) )){
firstAPI(param)
} else {
secondAPI(param)
}
...
So i solved it a lot of days ago, but i forgot to post my solution, so here it goes:
const handleExcelClick = (param: any) => {
if (typeof param === 'number') {
return firstAPI(param);
} else if (param.some((i: any) => typeof i === 'string')) {
return secondAPI(param);
}
};
i basically forgot to add a return to each statement, and there was also no point in filtering values based on just a filter method, so i used the "some" array method to just receive the values that return true if the condition is met, which is checking if the data type matches the statement
hope this helps anyone trying to do the same

React - Returning data from API

I know there are similar questions, but I can't find the answer.
First, please tell me if I'm doing something really wrong
I need to populate my state with data from an API call. This was working fine with code above:
export const GetPlanets = async () => {
const planets = await axios.get(`${BASE_URL}`).catch((e) => {
console.error(e);
})
return planets.data.results
}
But then I needed to make a second call to several links from one json response filed, and I managed to make it work (don't know if it is the correct approach, though)
const GetPlanets = async () => {
let planetas = {}
await axios.get(`${PLANETS_URL}`)
.then((p) => {
planetas = p.data.results
return axios.get(`${FILMS_URL}`)
}).then((f) => {
planetas.films.forEach((v, i) => {
planetas[i].film = f
})
})
})
.catch((e) => {
console.error(e);
})
return planetas
}
This is my component file, where I try to get the object, like I was doing before
useEffect(() => {
const fetchPlanetas = async () => { // ME TRYING...
const planetas = await GetPlanets()
setPlanetas(planetas)
setToShow(planetas[0])
};
fetchPlanetas()
}, [])
But all I get is undefined
You're getting an array of undefined because .map() needs a return value. In both your .map() callbacks, you are not returning anything.
const results = [1, 2, 3, 4]
const results2 = results.map(elem => {
elem = elem + 1
})
console.log(results2)
But, even if you did return something in your .map() callback, GetFilms(f) is asynchronous, so you would not get the results of GetFilms() mapped into the array as you would expect.
You have a couple of options:
If you have access to the API, send the films data along with the rest of the data when you do your first request.
Use async/await and Promise.all() to get responses.

Node JS nested Promise.all and map logic not working

I am trying to get a nested Promise.all * map logic to work. I keep getting undefined values when I reach getData2.
exports.getData = (param1, param2) => {
return getData0(param1, param2)
.then(data0 => Promise.all(data0.map(e => getData1(e.id))))
.then(data1 => Promise.all(data1.map(i => getData2(i.id))))
.then(data2 => data2.map(a => getData3(a.id)))
.catch(err => console.error(err.message));
};
P.S:
1. getData0 to getData1 return structures (e.g. { a: val1, b: val2 })
2. I assume the problem is in the way getData's are written. I suspect they should return promises. Can anyone give me a dummy example about a function that returns a structure wherein both elements of the (see a and b above) are obtained in an async way?
Thanks!
Firstly, all your getData* methods should return Promise objects if they are doing any asynchronous operations. (for e.g fetching data).
getData3 could be an exception to this since it doesn't look like anything needs to be done after all getData3 calls are completed.
If that's not the case you could use similar method for getData3 as for above.
e.g data2 => Promise.all(data2.map(a => getData3(a.id)))
Now let's look over the code line by line
exports.getData = (param1, param2) => {
return getData0(param1, param2)
.then(data0 => Promise.all(data0.map(e => getData1(e.id))))
// data0 here should be an Array
.then(data1 => Promise.all(data1.map(i => getData2(i.id))))
// 1. data1 will be an Array with results from each getData1 call
// mapped by index. for e.g [result1, result2, ...]
// 2. depending on the type of result (Object/Array) from getData1
// you should extract `id` from `i`(result item) and then
// trigger `getData2`using that `id`. I'm assuming the issue is
// here.
.then(data2 => data2.map(a => getData3(a.id)))
.catch(err => console.error(err.message));
};
As for Can anyone give me a dummy example about a function that returns a structure wherein both elements of the (see a and b above) are obtained in an async way?
I believe this should answer that How to use Promise.all with an object as input
I've marked Dhruv's answer as the valid one as it explains different concepts involved in the logic I described.
getData1 and getData2 indeed make calls to an async function (in my case: doSomeEWSwork below) while getData3 is sync.
function doSomeEWSwork(param) {
var ewsFunction = '.....';
var ewsArgs = ....;
return ews.run(ewsFunction, ewsArgs)
.then(result => result.something)
.catch(err => console.log(err.message));
}
My old getData1 and getData2 used to return structure objects (e.g. {a: val1, b: val2} instead of promises (needed for Promise.all). That was causing the async blocs to never execute/evaluate.
My new getData1 and getData2 have the following skeleton:
function getData1_or_2(param) {
var promise = new Promise(function(resolve, reject) {
doSomeEWSwork(param) // <- important: settle this using 'then'
.then(res => {
var ret = { a: res.val1, b: res.val2 };
resolve(ret);
}).catch(err => {
console.log(err.message);
reject(Error(err.message));
});
});
return promise;
}
So the main method (already included in my original question) works fine now since I am returning promises for async and object for sync.
exports.getData = (param1, param2) => {
return getData0(param1, param2)
.then(data0 => Promise.all(data0.map(e => getData1(e.id))))
.then(data1 => Promise.all(data1.map(i => getData2(i.id))))
.then(data2 => data2.map(a => getData3(a.id)))
.catch(err => console.error(err.message));
};
It's just the combination of nested promises, map and structure objects that confused me.
Thanks!

RxJS 5.0 "do while" like mechanism

I'm trying to use RxJS for a simple short poll. It needs to make a request once every delay seconds to the location path on the server, ending once one of two conditions are reached: either the callback isComplete(data) returns true or it has tried the server more than maxTries. Here's the basic code:
newShortPoll(path, maxTries, delay, isComplete) {
return Observable.interval(delay)
.take(maxTries)
.flatMap((tryNumber) => http.get(path))
.doWhile((data) => !isComplete(data));
}
However, doWhile doesn't exist in RxJS 5.0, so the condition where it can only try the server maxTries works, thanks to the take() call, but the isComplete condition does not work. How can I make it so the observable will next() values until isComplete returns true, at which point it will next() that value and complete().
I should note that takeWhile() does not work for me here. It does not return the last value, which is actually the most important, since that's when we know it's done.
Thanks!
We can create a utility function to create a second Observable that emits every item that the inner Observable emits; however, we will call the onCompleted function once our condition is met:
function takeUntilInclusive(inner$, predicate) {
return Rx.Observable.create(observer => {
var subscription = inner$.subscribe(item => {
observer.onNext(item);
if (predicate(item)) {
observer.onCompleted();
}
}, observer.onError, observer.onCompleted);
return () => {
subscription.dispose();
}
});
}
And here's a quick snippet using our new utility method:
const inner$ = Rx.Observable.range(0, 4);
const data$ = takeUntilInclusive(inner$, (x) => x > 2);
data$.subscribe(x => console.log(x));
// >> 0
// >> 1
// >> 2
// >> 3
This answer is based off: RX Observable.TakeWhile checks condition BEFORE each element but I need to perform the check after
You can achieve this by using retry and first operators.
// helper observable that can return incomplete/complete data or fail.
var server = Rx.Observable.create(function (observer) {
var x = Math.random();
if(x < 0.1) {
observer.next(true);
} else if (x < 0.5) {
observer.error("error");
} else {
observer.next(false);
}
observer.complete();
return function () {
};
});
function isComplete(data) {
return data;
}
var delay = 1000;
Rx.Observable.interval(delay)
.switchMap(() => {
return server
.do((data) => {
console.log('Server returned ' + data);
}, () => {
console.log('Server threw');
})
.retry(3);
})
.first((data) => isComplete(data))
.subscribe(() => {
console.log('Got completed value');
}, () => {
console.log('Got error');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.1/Rx.min.js"></script>
It's an old question, but I also had to poll an endpoint and arrived at this question. Here's my own doWhile operator I ended up creating:
import { pipe, from } from 'rxjs';
import { switchMap, takeWhile, filter, map } from 'rxjs/operators';
export function doWhile<T>(shouldContinue: (a: T) => boolean) {
return pipe(
switchMap((data: T) => from([
{ data, continue: true },
{ data, continue: shouldContinue(data), exclude: true }
])),
takeWhile(message => message.continue),
filter(message => !message.exclude),
map(message => message.data)
);
}
It's a little weird, but it works for me so far. You could use it with the take like you were trying.
i was googling to find a do while behavior, i found this question. and then i found out that doWhile takes in a second param inclusive boolean. so maybe you can do?:
takeWhile((data) => !isComplete(data), true)

subscribe while preserving order

I am trying to implement a auto-reconnect client for a server which receives a command and then replies with a single byte. However, the problem is that one cannot send any additional commands to the server while it is processing commands. So I need to somehow serialise the commands, is that possible to achieve in a pragmatic way in RxJS?
const onClient = new BehaviourSubject(...) // Auto-reconnecting client
function sendCommand(cmd) {
return onClient
.concatMap(client => {
client.write(cmd + '\r\n')
return Rx.Observable.fromEvent(client, 'data').take(1)
})
}
sendCommand('CMD1').subscribe(x => console.log(x))
sendCommand('CMD2').subscribe(x => console.log(x)) // Oops, sent a command while another one is active...
Here is one possible solution that lacks error handling and looks quite inefficient.
const input = new Rx.Subject()
const output = new Rx.Subject()
input.concatMap(({cmd, id})) => onClient
.filter(client => client != null)
.concatMap(client => {
client.write(cmd + '\r\n')
return Rx.Observable.fromEvent(client, 'data').take(1)
})
.map(value => ({value, id}))
.subscribe(output)
function sendCommand(cmd) {
const id = cuid()
input.onNext(id)
return output
.filter(res => res.id === id)
.map(res => res.value)
}
Any better ideas or suggestions on improvement?
Here is my gut instinct. I've only ever used JavaRX, and that just barely. Note that this assumes you want 1 invocation of CMD2 for every return of CMD1.
const onClient = new BehaviourSubject(...) // Auto-reconnecting client
function sendCommand(cmd) {
return onClient
.concatMap(client => {
client.write(cmd + '\r\n')
return Rx.Observable.fromEvent(client, 'data').take(1)
})
}
sendCommand('CMD1').subscribe(function(x) {
console.log(x);
sendCommand('CMD2').subscribe(y => console.log(y))
});
For what it's worth, you may want to consider using Promises for this stuff. My understanding of Rx is that it is useful for complex streams of async data, such as event streams. But if all you want is the async part, I believe Promises might be easier. We were considering using it on a Java project and decided it wasn't what we needed. See: When to Use Rx
I don't know what you are working on, but a command-response pattern seems to me like it might be better served by Promises, especially if you expect the lambda you're passing into subscribe to only be invoked once.
Here is the rather complicated try I ended up with:
import stampit from 'stampit'
import Rx from 'rx'
import cuid from 'cuid'
let input = new Rx.Subject()
let output = new Rx.Subject()
input
.concatMap(({fn, id}) => Rx.Observable
.defer(() => fn())
.map(value => ({value, id}))
.catch(error => Rx.Observable.return({error, id}))
.concat(Rx.Observable.return({id})))
.subscribe(output)
async function enqueue(fn) {
const id = cuid()
input.onNext({fn, id})
output
.filter(res => res.id === id)
.takeWhile(res => res.error || res.value)
.concatMap(res => res.error
? Rx.Observable.throw(res.error)
: Rx.Observable.return(res.value))
}
})
const onClient = new BehaviourSubject(...) // Auto-reconnecting client
function sendCommand(cmd) {
return enqueue(() => onClient
.concatMap(client => {
client.write(cmd + '\r\n')
return Rx.Observable.fromEvent(client, 'data').take(1)
}))
}
sendCommand('CMD1').subscribe(x => console.log(x))
sendCommand('CMD2').subscribe(x => console.log(x))

Categories

Resources