How To Make Parallel API calls in JSPython
It is quite a common scenario when you have to make multiple an API calls in parallel. Making parallel calls will
reduce the latency and improve the performance of your solution. Either you are looking to speed up a data loading
process
or perform some sort of load/stress testing.
In Javascript, you can spin up multiple `Promises` and then use the function Promise.all() to wait for all of them to succeed. Or you can use RxJS, streams or controlled callbacks
JSPython was designed to evaluate all instructions synchronously. Mainly because of simplicity, it looks like Python users - data engineers and analysts don't like the complexity that comes with Promises, Streams or callbacks. JSPython awaits/resolves all promises at its core. So, you won't have any Promise anywhere in the JSPython runtime (apart from some edge cases).
So, how to run code parallel, then?
The answer is RxJs. You can
use RxJs functions to
create observable sequences, then combine response values they emitted (with forkJoin) and convert them back to
Promise (which will be automatically evaluated in JSPython)
The following JSPython code shows this approach in action. This example makes five parallel requests and returns an array of responses along with the corresponding requestId
from 'rxjs' import forkJoin, lastValueFrom, map ids = [2, 7, 4, 9, 5] httpRequests$ = ids .map( requestId => httpRequest$("GET", "https://jsonplaceholder.typicode.com/posts/" + requestId) .pipe( map(r => {requestId, response: r.data}) ) ) return lastValueFrom(forkJoin(httpRequests$))
Let's go through the code.
- Import RxJs functions
- Create an array of ids.
- Use map function to transform an array of ids into an array of Observables.
- Use the httpRequest$ function to fetch data from a server. The asynchronous built-in function sends an HTTP request, and returns an Observable that emits the requested data when the response is received.
- Then we use observable's pipe(map(...)) and transform result, by adding a requestId and response
- Then we wait for all observables to complete and combine responses in the array. We use forkJoin for this
- Then convert observable into Promise with function lastValueFrom. Then, JSPython natively resolves Promise into a synchronous result
Run
Press `Run` and you will see the result
Working example
A working example can be found here
https://run.worksheet.systems/data-studio/app/guest/jspy-playground?file=http-calls%2Fparallel-http-requests.jspy
or if you are using chrome extention
chrome-extension://dkhnlgcpdiifkfjdjceogenclkdfbonh/index.html#/data-studio/app/guest/jspy-playground?file=http-calls%2Fparallel-http-requests.jspy
Sequential vs. Parallel API Calls
In another example, I want to show two different functions where one of them run_Sequential is making https requests sequential and function run_Parallel is doing same things but in parallel
from 'rxjs' import forkJoin, lastValueFrom, map ids = [2, 7, 4, 9, 5] async def run_Sequential(): data = [] for requestId in ids: response = httpGet("https://jsonplaceholder.typicode.com/posts/" + requestId) data.push({requestId, response}) return data async def run_Parallel(): httpRequests$ = ids .map( requestId => httpRequest$("GET", "https://jsonplaceholder.typicode.com/posts/" + requestId) .pipe( map(r => {requestId, response: r.data}) ) ) return lastValueFrom(forkJoin(httpRequests$)) if __env.entryFunction == '': return { sequential: run_Sequential(), parallel: run_Parallel() }
Working example
is here
https://run.worksheet.systems/data-studio/app/guest/jspy-playground?file=http-calls%2Fsequential-vs-parallel-http-calls.jspy.
Just Run this code and open chrome dev-tools - network tab. Where you can clearly see the difference
WORKSHEETS Data Studio
WORKSHEETS Data Studio is a Low Code Data Management Platform for Data Analysis, Data Processing, SQL Database Management and RAPID App Development.