Search code examples
javascriptnode.jsasync-awaittimeres6-promise

Print the data with the help of timer in ascending order


Here is my code to print input data in the order of the timer values. I used promises:

let a = [
    { data: 1000, timer: 3000 },
    { data: 2000, timer: 2000 },
    { data: 3000, timer: 1000 },
] 

const sleep = (m, d) =>
    new Promise((r,d) => setTimeout(r, m)).then((res) => {
        console.log(d);
    });

a.map(async (item) => {
   await sleep(item.timer, item.data);
});

The output should be

1000
2000
3000

but in the above attempt I am getting

3000
2000
1000

Why is it like that?


Solution

  • The code is doing what is expected, but you are getting confused with the values for data and timer: these are the same values (1000, 2000, 3000), but ordered in opposite directions. One can easily interpret the output data as a timeout. When the output happens in increasing order of timer, then the output will be decreasing when you output the data properties (instead of the timer properties).

    One way to clarify what happens is to not just print the data, but the whole object. Then it is clear that the objects are output in the correct order.

    Some unrelated remarks:

    • don't use .map when you are not creating an array.
    • don't use async/await when you are not doing anything after that await, nor with the promise that is returned.
    • don't use meaningless variable names like r and d, and avoid shadowing variables by using the same name for different scoped variables (like you have d).

    I have applied those remarks to the code, but the essence is in changing the second argument that is passed to sleep and the formatting in console.log

    let arr = [
        { data: 1000, timer: 3000 },
        { data: 2000, timer: 2000 },
        { data: 3000, timer: 1000 },
    ] 
    
    const sleep = (ms, obj) =>
        new Promise(resolve => setTimeout(resolve, ms)).then(() => {
            console.log(JSON.stringify(obj));
        });
    
    arr.forEach(item => sleep(item.timer, item));

    Another remark is to not use console.log in such a generic function as sleep. It is better to get that console.log out of there, and place it in the main loop. I did not do this in the above code, as that would hide too much what the core solution was to your question, but I provide it here as illustration:

    let arr = [
        { data: 1000, timer: 3000 },
        { data: 2000, timer: 2000 },
        { data: 3000, timer: 1000 },
    ] 
    
    const sleep = (ms, obj) => new Promise(resolve => setTimeout(resolve, ms));
    
    arr.forEach(item => sleep(item.timer).then(() => console.log(item)));

    Both in your code and this code, all three timers start at about the same time, before any asynchronous code gets executed. So the output of the object with timer equal to 1000 is expected to happen approximately 1 second after the start of the program, the one with 2000 is expected approximately 2 seconds after the start of the program, and the one with timer 3000 is expected to be output about 3 seconds after the start.