如何在Node.js和Express.js中改进这段代码,避免回调地狱

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

本文关键字:js 代码 地狱 回调 段代码 Node Express      更新时间:2023-09-26

我的一个控制器中有一个方法。控制器的目的是使用webshot包打印一组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(这是一个重要的细节,因为我需要为每个或类似的做一个)。

我读过关于异步包的文章。。。是否有更好的选项将此代码移动到类似async.proallel的位置?我可以在代码中使用yield吗?

谢谢!

由于您正在使用Promise,我推荐Promise.all.

它返回一个promise,该promise在可迭代参数中的所有promise都已解析时解析,或者以第一个传递的promise拒绝的原因拒绝。

看起来它解决了你的问题。

示例:

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

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

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

对于这样一个简单的示例,您不需要使用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);
    });
});

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

如果你不打算在这里添加更多的逻辑,就保持原样。

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