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

时间:2016-07-21 15:07:36

标签: javascript node.js express yield

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

这是有问题的代码:

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 package的信息...更好的选择是将此代码移至 async.parallel 之类的内容吗?我可以在代码中使用yield吗?

谢谢!

4 个答案:

答案 0 :(得分:2)

由于您使用的是Promise,我建议使用Promise.all

  

它返回一个promise,它在iterable参数中的所有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);
}

答案 1 :(得分:1)

您不需要使用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);
    });
});

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

老实说,你的代码看起来很好。

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

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