0

I am new to Promise concepts and trying to wrap my head around but now I`` am confused here

const request = require("request");
const cheerio = require("cheerio");
const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var url = require("url");


module.exports = {

    resturant: resturant,

};


var resturanttables = [];

function resturant(url, day) {

    return new Promise(function(resolve, reject) {
        request(url, function(error, response, html) {

            if (error) {
                return reject(error);

            } else if (response.statusCode !== 200) {

                return reject("Something is wrong! CINEMA")

            }
            httplogin("zeke", "coys", url, day);
            console.log(resturanttables, "i am here");

            resolve(resturanttables);

        });



    });
}

function httpafterlogin(url, cookie, day) {

    request.get({
            headers: {
                'content-type': 'text/html',
                'Cookie': cookie
            },
            url: url,
        },

        function(error, response, body) {



            console.log(day)
            var $ = cheerio.load(body);

            if (day === "Friday") {
                $(".WordSection2 p  span ").each(function(li) {
                    //  console.log(day, $(this).text())
                    resturanttables.push($(this).text());

                    console.log(resturanttables, "nside");
                });

            } else if (day === "Saturday") {
                $(".WordSection4 p span").each(function(li) {

                    resturanttables.push($(this).text())
                });

            } else {
                $(".WordSection6 p span").each(function(li) {

                    resturanttables.push($(this).text())


                });

            }

        });

}

function httplogin(username, password, urls, day) {

    request.post({
        headers: {
            'content-type': 'application/x-www-form-urlencoded'

        },
        url: urls,
        form: {
            "username": username,
            "password": password


        }
    }, function(error, response, body) {
        var cookie = response.caseless.dict['set-cookie'][0];
        var location = response;

        console.log(response.statusCode);
        cookie = cookie.substring(0, cookie.indexOf(';'));

        // httpafterlogin('http://vhost3.lnu.se:20080/dinner/'+response.headers.location, cookie);
        var newurls = url.resolve(urls, response.headers.location)
        httpafterlogin(newurls, cookie, day);

        // console.log(response.headers, "jdjdjjdjdjjdjdjdjjdjjdjdj")

    });

}

and then I call the function

loadPage.resturant("http://vhost3.lnu.se:20080/dinner/login", "Friday").then(function(data) {
    console.log(data, "did it work now ")
})

the problem is that it returns the empty array. But when i tried to check and console.log in the afterlogin function and i could see that the array was actually filled, but that code runs after the promise has been resolved. IN SHORT: how can I bound the resolve in restaurant promise not to send the data until the login function is completed?

in other words how can i get the filled array with information from afterlogin funtion?

TimoStaudinger
  • 41,396
  • 16
  • 88
  • 94
Alex
  • 57
  • 2
  • 10
  • I don't see anything wrong... maybe you left out a little bit of code or something? if `data` contains your array, then all is good. – Kevin B Nov 21 '16 at 16:00
  • `httpafterlogin` needs the same promise setup as the other. – Kevin B Nov 21 '16 at 16:01
  • But how Kevin B i tried but its not working – Alex Nov 21 '16 at 16:02
  • I don't see where you tried in your question, so i can't comment on how you tried wrongly. – Kevin B Nov 21 '16 at 16:03
  • Since you're new to promises, you will want to have a look at my [rules of thumb](http://stackoverflow.com/a/25756564/1048572). You violated multiple of them. – Bergi Nov 21 '16 at 22:12

3 Answers3

1

rewrite httplogin and httpafterlogin to return promises:

function httpafterlogin (url, cookie, day) {
    return new Promise(function (resolve, reject) {
        request.get({
            headers: {
                'content-type': 'text/html',
                'Cookie': cookie
            },
            url: url
        }, function (error, response, body) {
            if (error) {
                reject(error);
            } else {
                resolve(body);
            }
        });
    }).then(function (body) {
        console.log(day);
        var $ = cheerio.load(body);

        if (day === "Friday") {
            $(".WordSection2 p span").each(function (li) {
                //  console.log(day, $(this).text());
                resturanttables.push($(this).text());
                console.log(resturanttables, "nside");
            });
        } else if (day === "Saturday") {
            $(".WordSection4 p span").each(function (li) {
                resturanttables.push($(this).text());
            });
        } else {
            $(".WordSection6 p span").each(function(li) {
                resturanttables.push($(this).text());
            });
        }
    });
}


function httplogin(username, password, urls, day) {
    return new Promise(function (resolve, reject) {
        request.post({
            headers: {
                'content-type': 'application/x-www-form-urlencoded'
            },
            url: urls,
            form: {
                "username": username,
                "password": password
            }
        }, function(error, response, body) {
            if (error) {
                reject(error);
            } else {
                resolve(response);
            }
        });
    }).then(function (response) {

        var cookie = response.caseless.dict['set-cookie'][0];
        var location = response;

        console.log(response.statusCode);
        cookie = cookie.substring(0, cookie.indexOf(';'));

        var newurls = url.resolve(urls, response.headers.location)
        return httpafterlogin(newurls, cookie, day);
    });
}

then use .then like rsp suggested:

function resturant(url, day) {

    return new Promise(function(resolve, reject) {
        request(url, function(error, response, html) {
            if (error) {
                return reject(error);
            } else {
                resolve(response);
            }
        })
    }).then(function (response) {
        if (response.statusCode !== 200) {
            throw new Error("Something is wrong! CINEMA");     
        }
        return httplogin("zeke", "coys", url, day)
    }).then(function () {
        console.log(resturanttables, "i am here");
        return resturanttables;
    });
 }

this way, the block containing resolve(restautanttables) will not get called until httplogin completes

thedarklord47
  • 3,183
  • 3
  • 26
  • 55
  • Yes on your first sentence, but avoid the [`Promise` constructor antipattern](http://stackoverflow.com/q/23803743/1048572)! – Bergi Nov 21 '16 at 22:19
  • is this better? if not I would appreciate an explanation, I want to learn but the search results seemed to mostly involve superfluous wrapping of functions that already returned promises (rather than callback style functions in this example) – thedarklord47 Nov 22 '16 at 20:03
  • i believe this fixes the broken error chaining (the main negative of the anti-pattern to my understanding) – thedarklord47 Nov 22 '16 at 20:04
  • Thanks, yes. You might want to do a similar thing to the `new Promise` in `httplogin`, all the code in the callback should go in a separate `then` callback (so you only do `if (error) reject(error) else resolve(response)` in the unsafe nodeback). Especially since `httpAfterLogin` is async as well and should also be rewritten to return a promise. – Bergi Nov 22 '16 at 20:19
  • ah, didn't notice `afterhttplogin` was async as well. hows this edit? – thedarklord47 Nov 22 '16 at 20:57
  • Looks good to me :-) Now the last bit of polishing would be to move the declaration of `restauranttables` into the afterlogin function, and `return` it all the way down from there. – Bergi Nov 22 '16 at 21:03
  • haha I'll leave that to OP. thanks for the help though! You have always been nothing been helpful in JS issues. Always nice to take away new tips and things to avoid! – thedarklord47 Nov 22 '16 at 21:07
0

If you don't want the promise to get resolved before the login is completed then you will either have to make your httplogin function take a callback and run it like this:

httplogin("zeke", "coys", url, day, function (err) {
  if (err) {
    reject(err);
  } else {
    resolve(resturanttables);
  }
});

or make it return a promise and run it for example like this:

httplogin("zeke", "coys", url, day).then(function () {
  resolve(resturanttables);
}).catch(function (err) {
  reject(err);
});

There are more ways to do it with promises but this is the simplest way.

Either way you have to make your httplogin function signal its completion by either calling the callback that it takes as an argument or resolving the promise that it returns.

rsp
  • 107,747
  • 29
  • 201
  • 177
  • Actually that was the problem can I add promise in promise? i mean how can I wrap my http function? it will be really nice of you if you can show me how bkz I tried and it didn't work for me – Alex Nov 21 '16 at 18:13
0

Use promises throughout your code - you can simplify your code using the request-promise package in place of the request package. All requests become promises and the code is easier to read and maintain.

const rp = require("request-promise");
const cheerio = require("cheerio");
const url = require("url");

function resturant(url, day) {
  rp(url)
    .then(function(){
      // URL returned a 200 response
      // so attempt to perform login
      httplogin("zeke", "coys", url, day)
        .then(function (data) {
          // Promise is resolved here
          return data;
        });
    })
    .catch(function(error){
      // just throwing the error
      throw error;
    });
}

function httplogin(username, password, urls, day) {
  var options = {
    headers: {
      "content-type": "application/x-www-form-urlencoded"
    },
    uri: urls,
    form: {
      username: username,
      password: password
    },
    method: "POST",
    resolveWithFullResponse: true
  };
  rp(options)
    .then(function (response) {
      // POST succeeded
      // grab the cookie
      var cookie = response.caseless.dict['set-cookie'][0]
                           .substring(0, cookie.indexOf(';'));
      // get new url string
      var newurls = url.resolve(urls, response.headers.location);

      httpafterlogin(newurls, cookie, day)
        .then(function (tables) {
          return tables;
        })
        .catch(function (error) {
         // just throwing the error
         throw error;
        });

    })
    .catch(function (error) {
      // Login failure
      // just throwing the error
      throw error;
    });
}

function httpafterlogin(url, cookie, day) {
  var options = {
    headers: {
      "content-type": "text/html",
      "Cookie": cookie
    },
    uri: url,
    transform: function (body) {
      return cheerio.load(body);
    }
  };
  rp(options)
    .then(function ($) {
      // body has been transformed and
      // can now be processed with jQuery

      // initialise the tables array
      var tables = [];

      // DRY code
      // set default selector
      var selector = ".WordSection6 p span";
      // change the selector for Friday/Saturday
      if (day === "Friday") {
        selector = ".WordSection2 p  span ";
      } else if (day === "Saturday") {
        selector = ".WordSection4 p span";
      }
      // process the selected section
      $( selector ).each(function(li) {
        tables.push($(this).text())
      });

      // crawling complete
      return tables;
    })
    .catch(function (error) {
      // Crawling failure
      // just throwing the error
      throw error;
    });
}
Dan Nagle
  • 4,384
  • 1
  • 16
  • 28
  • You forgot to `return` the promises *everywhere*. – Bergi Nov 22 '16 at 00:23
  • Things like `.then(function (data) { return data; })` and `.catch(function(error){ throw error; });` are pointless - it works the same without them - and should simply be omitted. Especially if you want readable and maintainable code. – Bergi Nov 22 '16 at 00:24