最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Calling async function multiple times - Stack Overflow

programmeradmin7浏览0评论

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

function PageSpeedCall(callback) {
    var pagespeedCall = `://${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 = `://${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','facebook','stackoverflow'];
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.

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./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./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.','facebook.','stackoverflow.'];
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.

Share Improve this question edited Apr 6, 2018 at 23:12 Alex Ironside asked Apr 6, 2018 at 23:06 Alex IronsideAlex Ironside 5,05914 gold badges73 silver badges134 bronze badges 3
  • 3 love this But node just said it's stupid. LOL; Anyway, this is exactly why Async/Await has been brought to JavaScript. Refact to us that. – Randy Casburn Commented Apr 6, 2018 at 23:24
  • 1 You might find this helpful: stackoverflow./questions/37576685/… – Randy Casburn Commented Apr 6, 2018 at 23:29
  • 1 I'll need to read into the whole async and then thing. Thanks! – Alex Ironside Commented Apr 7, 2018 at 0:50
Add a ment  | 

3 Answers 3

Reset to default 10

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./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.','facebook.','stackoverflow.'])
  .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./pagespeedonline/v4/runPagespeed?url=https://${website}&strategy=mobile&key=${keys.pageSpeed}`;
  return await promisify(pagespeedCall);
}

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

  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, e.g. 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);
});
发布评论

评论列表(0)

  1. 暂无评论