Home > Software engineering >  Javascript async function in order
Javascript async function in order

Time:03-07

I can't seem to find how to solve my problem.

I am using socket to get live data, there is a .on() event which then executes another function async to get api data.

socket.on('data', (data) => { // listen to live events - event 1, 2, 3, 4, 5
  getAPIData(data);
});

async function getAPIData(data){
 const {symbol} = data;
 axios.get(`https://api.nasa.gov/planetary/apod?name=${symbol}`).then((newData)=>{
  console.log(newData); // console events  - 1,3,5,2,4 - I need this to be in the right order as they come in
 })
};

Socket.on() get multiple events per second and I need each for calculations. my problem now is that console.log(newData) is not logging in order as they come in because its promise based async.

and I really need the exact order they come in, how to do this in JavaScript nodejs.

CodePudding user response:

You can serialize the calls to axios and the logging of the results like this (but keep reading, we can also overlap the axios calls while still doing the output in order):

let queueLength = 0;
let sequencer = Promise.resolve();
function getAPIData(data){
    const {symbol} = data;
      queueLength;
    console.log("Added work item, queue length: "   queueLength);
    sequencer = sequencer
        .then(() => axios.get(`https://api.nasa.gov/planetary/apod?name=${symbol}`))
        .then((newData) => {
            console.log(newData);
        })
        .catch(error => {
            // ...handle/report error...
        })
        .finally(() => {
            --queueLength;
            console.log("Completed work item, queue length: "   queueLength);
        });
}

Live Example:

const rnd = (max) => Math.floor(Math.random() * max);

// Fake a sequence of socket.io events
for (let i = 0; i < 5;   i) {
    setTimeout(() => {
        getAPIData({symbol: i});
    }, 100 * (i   1));
}

// Fake axios
const axios = {
    get(url) {
        console.log(`Start ${url}`);
        const [, num] = /(\d )$/.exec(url);
        return new Promise(resolve => setTimeout(() => {
            console.log(`End ${url}`);
            resolve(num);
        }, rnd(800)));
    },
};

let queueLength = 0;
let sequencer = Promise.resolve();
function getAPIData(data){
    const {symbol} = data;
      queueLength;
    console.log("Added work item, queue length: "   queueLength);
    sequencer = sequencer
        .then(() => axios.get(`https://api.nasa.gov/planetary/apod?name=${symbol}`))
        .then((newData) => {
            console.log(newData);
        })
        .catch(error => {
            // ...handle/report error...
        })
        .finally(() => {
            --queueLength;
            console.log("Completed work item, queue length: "   queueLength);
        });
}
.as-console-wrapper {
    max-height: 100% !important;
}

Here's how that works:

  1. We start with a fulfilled promise in sequencer.
  2. Each time getAPIData is called, it waits for the previous promise to be settled before doing the axios call
  3. We assign the promise from the promise chain as the new value of sequencer so the next pass waits on it.

That does mean that each call to axios won't be done until the previous one finishes; there will never be any overlap. But if you just want the results in order, you can overlap the calls to axios, like this:

let queueLength = 0;
let sequencer = Promise.resolve();
function getAPIData(data){
    const {symbol} = data;
      queueLength;
    console.log("Added work item, queue length: "   queueLength);
    sequencer = Promise.all([
            sequencer,
            axios.get(`https://api.nasa.gov/planetary/apod?name=${symbol}`)
        ])
        .then(([, newData]) => { // Note the destructuring on this line
            console.log(newData);
        })
        .catch(error => {
            // ...handle/report error...
        })
        .finally(() => {
            --queueLength;
            console.log("Finished work item, queue length: "   queueLength);
        });
}

That way, the axios calls can overlap, but we still do the logging in order.

Live Example:

const rnd = (max) => Math.floor(Math.random() * max);

// Fake a sequence of socket.io events
for (let i = 0; i < 5;   i) {
    setTimeout(() => {
        getAPIData({symbol: i});
    }, 100 * (i   1));
}

// Fake axios
const axios = {
    get(url) {
        console.log(`Start ${url}`);
        const [, num] = /(\d )$/.exec(url);
        return new Promise(resolve => setTimeout(() => {
            console.log(`End ${url}`);
            resolve(num);
        }, rnd(800)));
    },
};

let queueLength = 0;
let sequencer = Promise.resolve();
function getAPIData(data){
    const {symbol} = data;
      queueLength;
    console.log("Added work item, queue length: "   queueLength);
    sequencer = Promise.all([
            sequencer,
            axios.get(`https://api.nasa.gov/planetary/apod?name=${symbol}`)
        ])
        .then(([, newData]) => { // Note the destructuring on this line
            console.log(newData);
        })
        .catch(error => {
            // ...handle/report error...
        })
        .finally(() => {
            --queueLength;
            console.log("Finished work item, queue length: "   queueLength);
        });
}
.as-console-wrapper {
    max-height: 100% !important;
}

Example output of that:

Added work item, queue length: 1
Start https://api.nasa.gov/planetary/apod?name=0
Added work item, queue length: 2
Start https://api.nasa.gov/planetary/apod?name=1
Added work item, queue length: 3
Start https://api.nasa.gov/planetary/apod?name=2
End https://api.nasa.gov/planetary/apod?name=2
Added work item, queue length: 4
Start https://api.nasa.gov/planetary/apod?name=3
End https://api.nasa.gov/planetary/apod?name=0
0
Finished work item, queue length: 3
Added work item, queue length: 4
Start https://api.nasa.gov/planetary/apod?name=4
End https://api.nasa.gov/planetary/apod?name=3
End https://api.nasa.gov/planetary/apod?name=1
1
Finished work item, queue length: 3
2
Finished work item, queue length: 2
3
Finished work item, queue length: 1
End https://api.nasa.gov/planetary/apod?name=4
4
Finished work item, queue length: 0

Notice that even though the call for name=2 finished before the call for name=0, 0 was logged before 2.

  • Related