简体   繁体   中英

Calling async function multiple times

So I have a method, which I want to call multiple times in a loop. This is the function:

function PageSpeedCall(callback) {
    var pagespeedCall = `https://www.googleapis.com/pagespeedonline/v4/runPagespeed?url=https://${websites[0]}&strategy=mobile&key=${keys.pageSpeed}`;
    // second call
    var results = '';
    https.get(pagespeedCall, resource => {
        resource.setEncoding('utf8');
        resource.on('data', data => {
            results += data;
        });
        resource.on('end', () => {
            callback(null, results);
        });
        resource.on('error', err => {
            callback(err);
        });
    });
    // callback(null, );
}

As you see this is an async function that calls the PageSpeed API. It then gets the response thanks to the callback and renders it in the view. Now how do I get this to be work in a for/while loop? For example

function PageSpeedCall(websites, i, callback) {
    var pagespeedCall = `https://www.googleapis.com/pagespeedonline/v4/runPagespeed?url=https://${websites[i]}&strategy=mobile&key=${keys.pageSpeed}`;
    // second call
    var results = '';
    https.get(pagespeedCall, resource => {
        resource.setEncoding('utf8');
        resource.on('data', data => {
            results += data;
        });
        resource.on('end', () => {
            callback(null, results);
        });
        resource.on('error', err => {
            callback(err);
        });
    });
    // callback(null, );
}

var websites = ['google.com','facebook.com','stackoverflow.com'];
for (let i = 0; i < websites.length; i++) {
    PageSpeedCall(websites, i);
}

I want to get a raport for each of these sites. The length of the array will change depending on what the user does.

I am using async.parallel to call the functions like this:

let freeReportCalls = [PageSpeedCall, MozCall, AlexaCall];

async.parallel(freeReportCalls, (err, results) => {
    if (err) {
        console.log(err);
    } else {
        res.render('reports/report', {
            title: 'Report',
            // bw: JSON.parse(results[0]),
            ps: JSON.parse(results[0]),
            moz: JSON.parse(results[1]),
            // pst: results[0],
            // mozt: results[1],
            // bw: results[1],
            al: JSON.parse(results[2]),
            user: req.user,
        });
    }
});

I tried to use promise chaining, but for some reason I cannot put it together in my head. This is my attempt.

return Promise.all([PageSpeedCall,MozCall,AlexaCall]).then(([ps,mz,al]) => {
    if (awaiting != null)
        var areAwaiting = true;
    res.render('admin/', {
        title: 'Report',
        // bw: JSON.parse(results[0]),
        ps: JSON.parse(results[0]),
        moz: JSON.parse(results[1]),
        // pst: results[0],
        // mozt: results[1],
        // bw: results[1],
        al: JSON.parse(results[2]),
        user: req.user,
    });
}).catch(e => {
    console.error(e)
});

I tried doing this:

return Promise.all([for(let i = 0;i < websites.length;i++){PageSpeedCall(websites, i)}, MozCall, AlexaCall]).
then(([ps, mz, al]) => {
    if (awaiting != null)
        var areAwaiting = true;
    res.render('admin/', {
        title: 'Report',
        // bw: JSON.parse(results[0]),
        ps: JSON.parse(results[0]),
        moz: JSON.parse(results[1]),
        // pst: results[0],
        // mozt: results[1],
        // bw: results[1],
        al: JSON.parse(results[2]),
        user: req.user,
    });
}).catch(e => {
    console.error(e)
});

But node just said it's stupid.

And this would work if I didn't want to pass the websites and the iterator into the functions. Any idea how to solve this?

To recap. So far the functions work for single websites. I'd like them to work for an array of websites.

I'm basically not sure how to call them, and how to return the responses.

It's much easier if you use fetch and async/await

const fetch = require('node-fetch');

async function PageSpeedCall(website) {
    const pagespeedCall = `https://www.googleapis.com/pagespeedonline/v4/runPagespeed?url=https://${website}&strategy=mobile&key=${keys.pageSpeed}`;
    const result = await fetch(pagespeeddCall);
    return await result.json();
}


async function callAllSites (websites) {
  const results = [];
  for (const website of websites) {
    results.push(await PageSpeedCall(website));
  }
  return results;
}

callAllSites(['google.com','facebook.com','stackoverflow.com'])
  .then(results => console.log(results))
  .error(error => console.error(error));

Which is better with a Promise.all

async function callAllSites (websites) {
  return await Promise.all(websites.map(website => PageSpeedCall(website));
}

Starting on Node 7.5.0 you can use native async/await:

async function PageSpeedCall(website) {
  var pagespeedCall = `https://www.googleapis.com/pagespeedonline/v4/runPagespeed?url=https://${website}&strategy=mobile&key=${keys.pageSpeed}`;
  return await promisify(pagespeedCall);
}

async function getResults(){
  const websites = ['google.com','facebook.com','stackoverflow.com'];

  return websites.map(website => {
    try {
      return await PageSpeedCall(website);
    }
    catch (ex) {
      // handle exception
    }
  })
}

Node http "callback" to promise function:

function promisify(url) {
  // return new pending promise
  return new Promise((resolve, reject) => {
    // select http or https module, depending on reqested url
    const lib = url.startsWith('https') ? require('https') : require('http');
    const request = lib.get(url, (response) => {
      // handle http errors
      if (response.statusCode < 200 || response.statusCode > 299) {
          reject(new Error('Failed to load page, status code: ' + response.statusCode));
        }
      // temporary data holder
      const body = [];
      // on every content chunk, push it to the data array
      response.on('data', (chunk) => body.push(chunk));
      // we are done, resolve promise with those joined chunks
      response.on('end', () => resolve(body.join('')));
    });
    // handle connection errors of the request
    request.on('error', (err) => reject(err))
  })
}

Make PageSpeedCall a promise and push that promise to an array as many times as you need, eg myArray.push(PageSpeedCall(foo)) then myArray.push(PageSpeedCall(foo2)) and so on. Then you Promise.all the array.

If subsequent asynch calls require the result of a prior asynch call, that is what .then is for.

Promise.all()

 Promise.all([promise1, promise2, promise3]).then(function(values) { console.log(values); });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM