【问题标题】:How to improve this code in Node.js and Express.js avoiding callback hell如何在 Node.js 和 Express.js 中改进此代码以避免回调地狱
【发布时间】:2023-03-22 20:50:02
【问题描述】:

我的一个控制器中有一个方法。控制器的目的是使用 webshot package 打印一个 url 数组。

这是有问题的代码:

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);


     });
});

这段代码运行没有问题……但我想做得更好。我不知道需要检查多少个 URL(这是重要的细节,因为我需要为每个或类似的 URL 做一个)。

我已经阅读了关于async package... 的更好的选择是将此代码移动到类似 async.parallel 的位置吗?我可以在代码中使用 yield 吗?

谢谢!

【问题讨论】:

  • 不知道为什么这个问题被否决了……

标签: javascript node.js express yield


【解决方案1】:

由于你使用的是 Promise,我推荐Promise.all

它返回一个当可迭代参数中的所有承诺都已解决时解决的承诺,或者以第一个被拒绝的承诺的原因拒绝。

好像解决了你的问题。

例子:

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);
}

【讨论】:

    【解决方案2】:

    对于这样简单的示例,您无需使用async。使用原生承诺:

    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);
        });
    });
    

    【讨论】:

    • @chemitaxis 用小的非关键修复更新代码;将此标记为回复。
    • 我不知道为什么)也许它不够抽象。
    【解决方案3】:

    这是一个基于内部函数的代码流示例:

    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);
        });
    });
    

    【讨论】:

    • 我更喜欢这种方式。
    【解决方案4】:

    老实说,您的代码看起来不错。

    如果您不打算在此处添加更多逻辑,请保持原样。

    可以做得更好的是,将其迁移到 ES6 语法并提取 anotherValue 函数,但我不知道这是否适用于您的情况。

    【讨论】:

      猜你喜欢
      • 2019-03-16
      • 2016-03-27
      • 2017-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多