简体   繁体   中英

How to call module function inside NodeJS createServer

I have two js file screenshot.js and main.js, I want to return JSON from screenshot.js to main.js, but it didn't return result, in screenshot.js file when you run console.log(this.result) you see result in console but when you call it in main.js file the result is empty, how can solve this problem.

This is my code.

screenshot.js

module.exports={
result: '',
run: function(url,w,h){
var phantom=require('phantom');
phantom.create().then(function(ph){
    ph.createPage().then(function(page){
        page.property('viewportSize',{width:w,height:h}).then(function(){
            page.open('http://' + url + '/').then(function(status){
                page.property('onLoadFinished').then(function(){
                    console.log(status);
                    if (!(status == 'success')){
                         this.result={'image':'no'};
                         this.result=JSON.stringify(this.result);
                        //  console.log(this.result);
                            page.close();
                    } else {
                this.result=page.renderBase64('jpeg').then(function(img){
                                this.result={'image': img};
                                this.result = JSON.stringify(this.result);
                                //  console.log(this.result);
                                page.close();
                        });
                    }
                });
            });
        });
    });
});
return this;
},
get: function(){
return this.result;
}
}

main.js

var http = require( 'http' );
var parsing = require( 'url' );
var screenshot = require( './screenshot' );
http.createServer( function( req, res ) {
var url, img, w, h, query_object, result;
url = req.url;
url = url.replace( '/', '' );
url = url.trim();
if ( !( url == 'favicon.ico' ) ) {
  console.log( url );
  query_object = parsing.parse( req.url, true ).query;
  console.log( query_object );
  res.writeHeader( 200, { "Content-Type": "text/html" } );
  w = parseInt( query_object.width );
  h = parseInt( query_object.height );
  result = screenshot.run( url, w, h ).get();
  console.log( result );
  res.end();
}
}).listen( 80, '127.0.0.1' );

console.log( 'server isn running....' );

Your operation is async. You can't fetch the result until it's done. You are calling .get() long before your async operations are done.You need to return a promise from .run() and use .then() on that, not store the result in the instance data with no way to tell when it's ready.

As a general rule, any time you are taking a result that is obtained inside an async callback and you are assigning it to some higher scoped variable, that's a major warning sign that you may be doing something wrong because code in higher scopes will have no idea when that value is valid. Instead, you need to either use the value inside the async callback, call some function from within that callback and pass that function the value or return the value in a promise so it can be consumed by the caller as the fulfilled value of the promise (that's what I changed your code to be below).

Here's a rewritten version that returns a promise and returns the result as the fulfilled value of the promise:

module.exports = {
    run: function (url, w, h) {
        var phantom = require('phantom');
        return phantom.create().then(function (ph) {
            ph.createPage().then(function (page) {
                page.property('viewportSize', {
                    width: w,
                    height: h
                }).then(function () {
                    page.open('http://' + url + '/').then(function (status) {
                        page.property('onLoadFinished').then(function () {
                            console.log(status);
                            if (status != 'success') {
                                page.close();
                                return JSON.stringify({'image': 'no'});
                            } else {
                                return page.renderBase64('jpeg').then(function (img) {
                                    page.close();
                                    return JSON.stringify({'image': img});
                                });
                            }
                        });
                    });
                });
            });
        });
    }
}

In a bit, I will post a better way to write this too that doesn't use so much promise nesting.

The, instead of calling .get() , you just use the returned promise like this:

var http = require( 'http' );
var parsing = require( 'url' );
var screenshot = require( './screenshot' );
http.createServer( function( req, res ) {
    var url, img, w, h, query_object, result;
    url = req.url;
    url = url.replace( '/', '' );
    url = url.trim();
    if ( !( url == 'favicon.ico' ) ) {
      console.log( url );
      query_object = parsing.parse( req.url, true ).query;
      console.log( query_object );
      res.writeHeader( 200, { "Content-Type": "text/html" } );
      w = parseInt( query_object.width );
      h = parseInt( query_object.height );
      screenshot.run( url, w, h ).then(function(result) {
          console.log( result );
          res.end();
      });
    }
}).listen( 80, '127.0.0.1' );

console.log( 'server isn running....' );

I don't have a means of testing this myself, but this should be a less nested version of your run() method. This uses chaining where possible, only nesting when following .then() handlers need access to prior results.

module.exports = {
    run: function (url, w, h) {
        var phantom = require('phantom');
        return phantom.create().then(function (ph) {
            return ph.createPage();
        }).then(function (page) {
            // nest other calls here so they all have access to the page argument
            return page.property('viewportSize', {width: w, height: h }).then(function () {
                return page.open('http://' + url + '/');
            }).then(function (status) {
                return page.property('onLoadFinished').then(function() {return status;});
            }).then(function (status) {
                console.log(status);
                if (status != 'success') {
                    page.close();
                    return JSON.stringify({'image': 'no'});
                } else {
                    return page.renderBase64('jpeg').then(function (img) {
                        page.close();
                        return JSON.stringify({'image': img});
                    });
                }
            });
        });
    }
}

Note: I think you are also missing error handling that calls page.close() in case of errors.

Here's a version with error handling for closePage() added so anytime a page was opened, it will be closed no matter how we leave this code, even if errors occurred:

module.exports = {
    run: function (url, w, h) {
        var phantom = require('phantom');
        return phantom.create().then(function (ph) {
            return ph.createPage();
        }).then(function (page) {
            var pageOpen = false;

            function closePage(val) {
                if (pageOpen) {
                    page.close();
                }
                return val;
            }

            // nest other calls here so they all have access to the page argument
            return page.property('viewportSize', {width: w, height: h }).then(function () {
                return page.open('http://' + url + '/');
            }).then(function (status) {
                pageOpen = true;
                return page.property('onLoadFinished').then(function() {return status;});
            }).then(function (status) {
                console.log(status);
                if (status != 'success') {
                    return JSON.stringify({'image': 'no'});
                } else {
                    return page.renderBase64('jpeg').then(function (img) {
                        return JSON.stringify({'image': img});
                    });
                }
            }).then(function(closePage), function(err) {
                closePage();
                throw err;
            });
        }):
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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