#Possibility of Typescript multithread querying an external local Kotlin's library?

48 messages · Page 1 of 1 (latest)

abstract scaffold
#

Is there a possibility on Typescript to create a multithread where each thread calls an hook who asks to an external local Kotlin's library to run a HttpURLConnection request?
I mean, like having on TS an array of IDs and for each ID, instantiate a Thread which runs an hook to ask the Kotlin library to execute a HttpURLConnection for that ID and get back as promise the result of that HttpURLConnection....

digital pendant
#

seems like you have a very specific implementation in mind, but what's the big picture? what are you trying to do?

#

i don't know what "create a multithread" means, but you probably don't need threads at all. normal Promise-based code should be sufficient to make the requests happen concurrently

abstract scaffold
#

@digital pendant first thank you for your answer.... my goal would be having an array of IDs (which are some custom devices to query) and at Typescript side , while querying each one with a HttpURLConnection in parallel, get the informations back and show them with a object in a FlatList .... I was thinking about threads so supposing to have 10 IDs I would query all them in parallel and get back informations to cut waiting time.....

digital pendant
#

by "big picture" i meant zooming out even further. you're trying to write a program that makes HTTP requests, to do what exactly?

#

what kind of "custom devices"?

abstract scaffold
digital pendant
#

why do you need to involve this Kotlin library? is there a reason you couldn't make the HTTP requests directly from your TypeScript code?

abstract scaffold
digital pendant
#

JavaScript (and therefore TypeScript) have async I/O built in. you don't need threads to make concurrent HTTP requests

abstract scaffold
digital pendant
#

here's how you would make a bunch of concurrent requests directly:

const responses = await Promise.all([
  fetch("https://192.168.3.120...."),
  fetch("https://192.168.3.121...."),
  fetch("https://192.168.3.122...."),
  fetch("https://192.168.3.123...."),
  // etc
])
digital pendant
#

is this code going to be running in a web browser?

abstract scaffold
#

that .fetch is similar to Java&Kotlin's HttpURLConnection?

abstract scaffold
digital pendant
digital pendant
#

i don't know if/how react native implements fetch, but it probably just uses the same HTTP stack as native Java/Kotlin under the hood

#

but your life will be simpler if you can keep all the code in TypeScript rather than trying to communicate across languages

abstract scaffold
digital pendant
abstract scaffold
abstract scaffold
# digital pendant here's how you would make a bunch of concurrent requests directly: ```ts const r...

hello @digital pendant , sorry if I pick again this thread...but referring to your answer about multiple fetchs , in Java I use to run 26 ExecutorService threads where inside each of ExecutorService I call 11 Runnable threads so in this way I have parallel threads to improve performance on querying SocketAddress devices.... in Typescript , should be improved doing like the same, for example having 255 fetch() total so making 15 separate promises where running 17 fetch() in each promise OR is it the same performance if I'd do like PromiseAll as like as in your sample and , inside each them using a for (i=1;i<=255;i++) { fetch(ip+"."+i,....) .... } ? Thanks in advance

digital pendant
#

for your other example too, for (…) { fetch(…) } is going to spawn all of those promises and then forget about them (they'll run concurrently) while for (…) { await fetch(…) } is going to wait for each promise to complete before starting the next fetch

#

i also don't understand why you would want multiple ExecutorServices with your Java version, but my Java is pretty rusty so maybe there's something i'm forgetting

abstract scaffold
# digital pendant for your other example too, `for (…) { fetch(…) }` is going to spawn all of thos...

In Java, using an ExecutorService , I am running concurrently his work with one network because I could be connected simultaneously on both networks, WIFI and Hotspot which is possible with some mobile phones,... like executorservicethread.execute ( new pollingNetworkThread(....) ) where inside pollingNetworkThread I pass the mask of wifi network for example like 192.168.3. where it would be cicled inside for each IP calling in a thread first a LOGIN so if successful and obtaining the session_id, then I call another API to same IP as **CONFIGURATION **and if I get result, then another API as **STATUS **to obtain informations of device as json... and my idea was to put all informations to a structure which would be automatically shown as template in a FlatList.... got what I mean sort of?..
On typescript I was starting with something like :

const result: string[] = jsonConnections.flatMap(f => f.Address !== null ? f.Address.substring(0, f.Address.lastIndexOf(".")) : []); // where I get the network which is active and obtaining the mask like 192.168.3. 
result.forEach(element => {
    let th_Counter = 1
    Promise.all(
        let urls : string[]
        for (i=th_Counter;i<th_Counter+17;i++)
            urls.push(element+i.toString()
        
        urls.map(u => Promise.all( useGetData(LOGIN, u, .... ) )
                .then(response => (
                    if ( isJson(response) ) {
                        Promise.all ( useGetData(CONFIGURATION, u, .... ) )
                        .then( response => (
                            if (isJson(response) ) {
                                completeDataSingleDevice(response)
                                Promise.all( useGetData(STATUS, u, ....) )
                                .then( response => (
                                    if ( isJson(response) ) {
                                        completeDataSingleDevice(response)
                                    }                                
                                )
                            }
                        )
                    }
                )
    )
    th_Counter=+15    
})

something like that where useGetData() would be an export function useGetData where I compose the URL to use inside a fetch where I query that device:

const response = await fetch(urlCommand, {
    method: "GET",
    headers: { "Content-Type": "application/json" }
});
const json = await response.json()
setData(json)

maybe it's not the right way to make concurrently?...

digital pendant
#

sorry but i'm having trouble understanding your writing style. maybe there's a language barrier? it might help if you could use more separate sentences

here's what i think your use case is:

  • you have a list of IP addresses
  • separately for each of those IP addresses, you want to:
    • log in
    • fetch configuration
    • fetch status
  • you want to collect all of the responses across all IP addresses into a single flat array

questions:

  • do i have the basic use case right?
  • how do LOGIN, CONFIGURATION, and STATUS depend on eachother? for example, do you have to include the session_id you get back from LOGIN in the CONFIGURATION request?
abstract scaffold
#

Hello @digital pendant ... the code I wrote was not precise for sure and I surely missed something or have to rewrite better...
Anyway yes , my goal is : once I obtain the mask of enabled and connected networks like WIFI and/or HOTSPOT/ROUTE (how it is called anyway) like *192.168.3. * with :

const result: string[] = jsonConnections.flatMap(f => f.Address !== null ? f.Address.substring(0, f.Address.lastIndexOf(".")) : []);

and for each network , doing a polling to all IP between 1 and 255 and if at least one logs in (CommandTypes.LOGIN) so having initialized in a .d.ts file an array of elements based to an interface already , I'd start to prepare a single element with Partial<> to store some data and , yes, I get the session_id from the LOGIN, which I use in next two fetchs because are two different kind of API..
I was trying to rewrite the code using Promise.all() so in case of success then initialize a new single element with partial data to be available for next API call where I need session_id... so I was starting to rewrite as :

result.forEach(element => {
    let urls : string[] = []
    let currentUrl : string = ""
    for (let i=1;i<255;i++) {
        urls.push(element+i.toString().toString())
    }
    Promise.all(urls.map(u =>
        useGetData(CommandTypes.LOGIN, u,username, password )))
            .then(responses => {
            responses.map(r => {
                if (r.commandDone) {
                    // Login success
                    const single: [Partial<ISingleType>] = [{
                        IPMacAddress: u.toString(),
                        SessionID: JSON.parse(r.jsonData).filter(el => el.include("session_id").toString())
                    }]
                    ElementsList.push(Single);
                    useGetData(CommandTypes.GETCONFIGURATION,u, Object.values(conf_codes).join(',').toString(), single[0]?.SessionID?.toString())
                }
            })
        })
})

but at the moment I am tryng to understand how to pass the current url , u, inside response and reuse the current element values ...on the other hand I assume using Promise.all is like doing a for(...) { fetch() } , right?...

digital pendant
#

on the other hand I assume using Promise.all is like doing a for(...) { fetch() }, right?
not really, though i'm not sure how literally to take the analogy you're making there

#

let's forget about everything leading up to building urls since that's irrelevant

#

you have urls, which as far as i can tell is an array full of IP addresses, right? edit: actually i guess you have a separate array of IP addresses per element in result, but i'm not sure why. i suspect you could build one single list of IP addresses by combining stuff from all elements of result

digital pendant
abstract scaffold
#

if that result variable will have for example 192.168.0. for Wifi and 192.168.170. for Hotspot/Router , then with result.forEach I am going to fill urls with a for () so, yes I'll have like: [192.168.01, 192.168.0.2, ... , 192.168.0.255 , 192.168.170.1 , 192.168.170.2 , ... , 192.168.170.255] and first I have to do a LOGIN to get the session_id which is needed to call both CONFIGURATION and STATUS (they differ just about action2 parameter, GetConfiguration or GetStatus, in URL I compose inside export function useGetData(....) , because there I'd manage also a possible AbortController like if connection goes away...

digital pendant
#

okay, so if i understand correctly you have two opportunities to do things concurrently:

  • you can do all handling for each individual IP address concurrently because they don't depend on eachother at all
  • for each IP address, you can do the CONFIGURATION and STATUS requests concurrently because they don't depend on eachother (but both depend on LOGIN, so that needs to happen first)
    does that sound right?
#

in addition to answering that question ☝️, can you also show me the type signature of useGetData please?

abstract scaffold
#

HI @digital pendant , yes you're right about calling all IPs concurrently... I guess just calling a Promise.all would all inside for each IP in the urls array and getting filled back the interface structure for each IP , practically a var elementsList : ISingleElementType[] = [] declared globally in a .d.ts file , which would be used in a FlatList to show all elements from a component template... that is my idea...
About useGetData I was thinking something like that:

#
export function useGetData (
    commandType:CommandTypes,
    ip: string,
    ...params: string[]
) {
    const [currentIp, setCurrentIP] = useState<string>("") // so I can pass the current ip to the response after LOGIN to call GETCONFIGURATION and GETSTATUS
    const [jsonData, setData] = useState<string>("") // to pass the normalized JSON
    const [commandDone, setCommandDone] = React.useState<boolean>(false) // if the execution of this fetching was completed or not
    const [errorResponse, setError] = useState<string>("") // eventual error
    const controller = useRef<AbortController | null>(null); // still to manage
    setCurrentIP(ip)
    const urlCommand = `....` // command construction with arguments passed
    useEffect(() => {
        fetchData()
    }, [])
    const fetchData = async () => {
        await fetch(urlCommand, {
            method: "GET",
            headers: { "Content-Type": "application/json" }
        }).then(res =>{
            res.json().then(data => {
                switch (commandType) {
                    case CommandTypes.LOGIN:{
                        setData(data)
                    }
                    break;
                    case CommandTypes.GETCONFIGURATION: {
                        // manage returned JSON and normalize it to return another JSON and fill the single element data,
                        // like forEach element of the single element fill it with the relative value key in normalized json
                    }
                    break;
                    case CommandTypes.GETSTATUS : {
                        // same as GETCONFIGURATION
                    }
                    break;
                    default : { }
                    break;
                }
                setCommandDone(true)
            })
        }).catch(error => {
            setError(error)
            setCommandDone(false)
        })
    }
    return {currentIp, commandDone, jsonData, errorResponse}
}
digital pendant
#

hmm, if useGetData doesn't return a Promise, then you never need to call Promise.all at usage sites. every time you call that version of useGetData it's firing-and-forgetting those requests. the next line of code after the call will happen immediately while the fetch is still happening in the background

#

this also means you have no way to sequence things from the call site (for example no way to ensure that LOGIN completes before the other requests begin)

#

i'm guessing that's not actually desired and it's a mistake for useGetData to not be returning a Promise?

digital pendant
digital pendant
#

i sketched up an example of what i think is the flow you want. if i understand everything correctly this will give you as much concurrency as is possible for your use case:

mossy tideBOT
#
mkantor#0

Preview:```ts
...
const operationsForAllIps = allIps.map(async ip => {
const loginResponse = await fetch(makeLoginUrl(ip), {
// ...
})

const configurationOperation = fetch(
makeConfigurationUrl(ip),
{
// ... (includes session id from loginResponse)
}
)
const statusOperation = fetch(makeStatusUrl(ip), {
// ... (includes session id from loginResponse)
})

// do configuration and status operations concurrently:
return Promise.all([
configurationOperation,
statusOperation,
])
})
...```

digital pendant
#

that obviously elides a bunch of details you've had in your examples, but i just want to focus on the overall flow. once you understand that you should be able to add things back in