Search code examples
javascriptjquerypromisees6-promise

Convert nested AJAX call to sequential Promises


I have code which is successfully running AJAX calls in sequence (a crude internet latency test for my employees):

const start = new Date().getTime();
const testUrl = "our server URL";
const start = new Date().getTime();
$.ajax(testUrl + '?delay=0.0&length=100').done(function(){
  $.ajax(testUrl + '?delay=0.1&length=1000').done(function(){
    $.ajax(testUrl + '?delay=0.2&length=10000').done(function(){
      $.ajax(testUrl + '?delay=0.1&length=100000').done(function(){
        $.ajax(testUrl + '?delay=0.2&length=10000').done(function(){
          $.ajax(testUrl + '?delay=0.1&length=1000').done(function(){
            $.ajax(testUrl + '?delay=0.0&length=100').done(function(){
              $.ajax(testUrl + '?delay=0.1&length=10').done(function(){
                const end = new Date().getTime();
                internetTestTime = end - start;
                console.log('Your internet test time: ' + internetTestTime + 'ms')
                })
            })
          })
        })            
      })
    })
  })
});

I would like to switch this to use Promises that run in sequence. Here is the progress so far:

const start = new Date().getTime();
const testSuiteInputs = [
    {delay: 0.5, length: 100},
    {delay: 0.4, length: 500},
    // more tests here
];
let testSequence = Promise.resolve();
testSuiteInputs.forEach(testSuiteInput => {
    // 🌟 something goes here...
});
testSequence.then(() => {
    const end = new Date().getTime();
    internetTestTime = end - start;
    console.log('Your internet test time: ' + internetTestTime + 'ms')
});

What do I need to put at 🌟 above to make these Promises run sequentially?


Solution

  • Well, the simplest way would be to switch your .forEach() loop to a plain for loop inside an async function and then use await:

    async function run() {
        const start = new Date().getTime();
        const testSuiteInputs = [
            {delay: 0.5, length: 100},
            {delay: 0.4, length: 500},
            // more tests here
        ];
        for (let item of testSuiteInputs) {
            let url = `${testUrl}?delay=${item.delay}&length=${item.length}`;
            let result = await $.ajax(url);
            // process result here
        }
    }
    
    run().then(finalResult => {
        console.log("all done");
    }).catch(err => {
        console.log(err);
    });
    

    To do it without using async/await, a common design pattern for sequencing asynchronous access to an array is to use .reduce() where you just chain promises together:

    const start = new Date().getTime();
    const testSuiteInputs = [
        {delay: 0.5, length: 100},
        {delay: 0.4, length: 500},
        // more tests here
    ];
    
    testSuiteInputs.reduce((p, item) => {
        return p.then(() => {
            // when previous ajax call finished, start the next one
            let url = `${testUrl}?delay=${item.delay}&length=${item.length}`;
            return $.ajax(url).then(result => {
                // process individual result here
            });
        });
    }, Promise.resolve()).then(finalResult => {
        console.log("all done");
    }).catch(err => {
        console.log(err);
    });;