简体   繁体   English

如何在Node.js和Express.js中改进此代码以避免回调地狱

[英]How to improve this code in Node.js and Express.js avoiding callback hell

I have a method in one of my controller. 我的一个控制器中有一个方法。 The purpose of the controller, is print an array of urls using webshot package . 控制器的目的是使用webshot package打印URL数组。

This is the code in question: 这是有问题的代码:

router.post('/capture', function (req, res, next) {

  //Check params remove 

  var json = JSON.parse(req.body.data);

  var promise = new Promise(function (resolve, reject) {

    var totalImages = Object.keys(json).length;
    var arrayListUrlImages = new Array(totalImages);
    var counter = 0;           
    var completeDir = dir + ''; //Directory URL    

    for (var value of json) {    
      var url = 'http://example.com/' + id + '/' + value.anotherValue;
      var folder = completeDir + id + '/' + value.anotherValue + '.jpg';

      //Options for capturing image
      var options = {
        renderDelay: 1000,
        quality: 100,
        phantomConfig:
        {
          'local-to-remote-url-access': 'true',
          'ignore-ssl-errors': 'true'
        }       
      };

      var anotherValue = value.anotherValue;

      (function (anotherValue) {

          webshot(url, folder, options, function (err) {
        // screenshot now saved            

        if (err === null) {

          var urlImage = "http://example.com/images/" + id + "/" + anotherValue + ".jpg";
          arrayListUrlImages.push(urlImage);
          counter++;
          console.log("Counter: " + counter);

          if (counter === totalImages) {                
            resolve(arrayListUrlImages);
          }
        }
        else {
          reject(err);
        }
      });    
      })(anotherValue);


    }




  }).then(function (arrayImages) {

    res.send(arrayImages);   


  }).catch(function (errorVale) {
    res.send(null);


     });
});

This code is working without problems... but I would like to do better. 这段代码可以正常工作...但是我想做得更好。 I don't know how many URLs need to check ( this is important detail because I need to do a for each or similar ). 我不知道需要检查多少个URL( 这很重要,因为我需要为每个 URL 或相似的 URL进行检查)。

I have read about async package ... Is better option move this code to something like async.parallel ? 我已经阅读了有关异步包的信息 。更好的选择是将此代码移动到async.parallel之类的东西吗? Can I use yield in my code? 我可以在代码中使用yield吗?

Thanks! 谢谢!

Since you are using Promise, I recommend Promise.all . 由于您正在使用Promise,因此我建议您Promise.all

It returns a promise that resolves when all of the promises in the iterable argument have resolved, or rejects with the reason of the first passed promise that rejects. 当可迭代参数中的所有promise已解决时,它返回一个promise,该promise会被解决;如果第一个传递的promise被拒绝,则它会被拒绝。

Seems like it solves your problem. 似乎可以解决您的问题。

Example: 例:

downloadOne = url => new Promise(resolve => {
   webshot(url, ....., (err, res) => resolve(res));
})

router.post('/capture', function (req, res, next) {
    var urls = JSON.parse(req.body.data);
    Promise.all(urls.map(downloadOne)).then(req.send);
}

You don't need to use async for such simple example. 对于这样简单的示例,您无需使用async Use native promises: 使用本机承诺:

router.post('/capture', function (req, res, next) {

    //Check params remove 

    const json = JSON.parse(req.body.data);

    Promise.all(Object.getOwnPropertyNames(json).map((key) => {
        var value = json[key];

        var url = 'http://example.com/' + id + '/' + value.anotherValue;
        var folder = completeDir + id + '/' + value.anotherValue + '.jpg';

        //Options for capturing image
        var options = {
            renderDelay: 1000,
            quality: 100,
            phantomConfig:
            {
                'local-to-remote-url-access': 'true',
                'ignore-ssl-errors': 'true'
            }       
        };

        return new Promise((resolve, reject) => {
            webshot(url, folder, options, function (err) {
                if (err) {
                    reject(err);
                    return;
                }

                var urlImage = "http://example.com/images/" + id + "/" + anotherValue + ".jpg";
                resolve(urlImage);
            }
        });
    }))
    .then((listOfUrls) => {
        res.json(listOfUrls); // List of URLs
    }, (error) => {
        console.error(error);
        res.json(null);
    });
});

This is an example of code flow based on inner functions: 这是基于内部函数的代码流的示例:

router.post('/capture', function (req, res, next) {
    // Definitions

    // Load image
    function loadImage(value) {
        var url = 'http://example.com/' + id + '/' + value.anotherValue;
        var folder = completeDir + id + '/' + value.anotherValue + '.jpg';

        //Options for capturing image
        var options = {
            renderDelay: 1000,
            quality: 100,
            phantomConfig:
            {
                'local-to-remote-url-access': 'true',
                'ignore-ssl-errors': 'true'
            }       
        };

        return webshotPromise(url, folder, options);
    }

    // Load whebshot as a promise
    function webshotPromise(url, folder, options) {
        return new Promise((resolve, reject) => {
            webshot(url, folder, options, function (err) {
                if (err) {
                    reject(err);
                }

                var urlImage = "http://example.com/images/" + id + "/" + anotherValue + ".jpg";
                resolve(urlImage);
            }
        });
    }

    // The method flow
    const json = JSON.parse(req.body.data);

    // Get json keys and iterate over it to load
    Promise.all(
        Object.getOwnPropertyNames(json).map(key => loadImage(json[key]))
    )
    // Got list of urls
    .then((list) => {
        res.json(list); 
    }, (error) => {
        console.error(error);
        res.json(null);
    });
});

Honestly, your code looks fine. 老实说,您的代码看起来不错。

If you are not going to add more logic here, leave it as it is. 如果您不想在此处添加更多逻辑,请保持原样。

What can be done better, is migrating it to ES6 syntax and extracting anotherValue function but I am not aware if this applicable for your case. 可以做得更好的方法是,将其迁移到ES6语法并提取anotherValue函数,但我不知道这是否适用于您的情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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