3
var express = require('express');
var GoogleUrl = require('google-url');
var favicon = require('serve-favicon');
var mongo = require('mongodb').MongoClient;
var app = express();
var db;
var googleUrl = new GoogleUrl({key: 'AIzaSyB5i1sdnt6HwzrvsPTBu0FbPiUZrM_BCsk'});
var PORT = 8080;

mongo.connect('mongodb://localhost:27017/url-shortener', function(err, newDb){
   if(err){
       throw new Error('Database failed to connect');
   } else{
       console.log('Successfully connected to MongoDB on port 27017');
   }
   db=newDb;
   db.createCollection('sites', {
      autoIndexID: true 
   });
});

app.use(favicon(__dirname+'/public/favicon.ico'));

app.get('/new/*', function(req, res){
    var doc;
   console.log('This is the url: '+req.params[0]);
   googleUrl.shorten(req.params[0], function(err, shortUrl){
       if(err){
           console.log(err);
       }else{
           console.log(shortUrl);
       }
       doc = check_db(req.params[0], shortUrl, db);
   });

The below res.json statement runs and returns an undefined variable before the other functions have a chance of returning a value.

   res.json(doc);
});


app.listen(process.env.PORT, function(){
    console.log('Express listening on: '+PORT);
});

function check_db(longUrl, shortUrl, db){
    db.collection('sites').findOne({
        'longUrl': longUrl, 
        'shortUrl': shortUrl
    }, function(err, doc){
        if(err){
            console.log(err);
        }if(doc){

The res.json statement is executed before the below statements are executed and a value can be returned.

            console.log('This site already exists on the database');
            return doc;
        }else{
            save_db(longUrl, shortUrl, db);
        }
    });

}

function save_db(longUrl, shortUrl, db){
    db.collection('sites').insert({
        'longUrl': longUrl, 
        'shortUrl': shortUrl
    }, function(err, doc){
        if(err){
            throw err
        }else{
            console.log(doc);
        }
        db.close();
    });
}

In the above code, the res.json statement executes before the functions defined below the GET request have a chance to complete execution, with the result that res.json returns an undefined variable. I know that I have to implement asynchronous functionality into my app (potentially promises?), but I am at a complete loss as to how to do so!

0

2 Answers 2

1

A callback is just an argument, in a function call, so

googleUrl.shorten(req.params[0], function(err, shortUrl){
   if(err){
       console.log(err);
   }else{
       console.log(shortUrl);
   }
   doc = check_db(req.params[0], shortUrl, db);
});
res.json(doc);

behaves just like

foo(a, b);
bar();

Your call to #googleUrl.shorten() is immediately followed by your call to #res.json(), just like the call to #foo() is immediately followed by a call to #bar().

Your callback function:

function(err, shortUrl){
   if(err){
       console.log(err);
   }else{
       console.log(shortUrl);
   }
   doc = check_db(req.params[0], shortUrl, db);
}

Is executed asynchronously, meaning it does not interrupt regular control flow. When you need to postpone execution of a statement like #res.json(doc), you must make sure that it's execution is also taken out of regular control flow. To do that, you'll need to accept a callback argument in both #check_db() and #save_db(). The new function signatures will look like this:

function save_db(longUrl, shortUrl, db, callback)

and

function check_db(longUrl, shortUrl, db, callback)

Then pass a callback function, which accepts an argument 'doc', and executes res.json(doc), to your db functions. eg:

doc = check_db(req.params[0], shortUrl, db, function(doc){
  res.json(doc);
});

Note: You'll simply pass 'callback' to #save_db() from #check_db().

Finally, call the callback, rather than returning a value, once your asynchronous functions have completed execution. eg:

db.collection('sites').findOne({
    'longUrl': longUrl, 
    'shortUrl': shortUrl
}, function(err, doc){
    if(err){
        console.log(err);
    }if(doc){
        console.log('This site already exists on the database');
        callback(doc);
    }else{
        save_db(longUrl, shortUrl, db, callback);
    }
});

I've left the changes to #save_db() up to you. Good luck!

Sign up to request clarification or add additional context in comments.

Comments

0

Change check_db and save_db to be async

function check_db(longUrl, shortUrl, db, cb){
    db.collection('sites').findOne({
        'longUrl': longUrl, 
        'shortUrl': shortUrl
    }, function(err, doc){
        if(err){
            console.log(err);
            process.nextTick(function(){cb(err)});
        }else if(doc){
            console.log('This site already exists on the database');
            process.nextTick(function(){cb(null, doc)});
        }else{
            save_db(longUrl, shortUrl, db, cb);
        }
    });

}

function save_db(longUrl, shortUrl, db, cb){
    db.collection('sites').insert({
        'longUrl': longUrl, 
        'shortUrl': shortUrl
    }, function(err, doc){
        db.close();
        process.nextTick(function(){cb(err, doc)});
    });
}

And use like this :

app.get('/new/*', function(req, res){
    var doc;
   console.log('This is the url: '+req.params[0]);
   googleUrl.shorten(req.params[0], function(err, shortUrl){
       if(err){
           console.log(err);
       }else{
           console.log(shortUrl);
       }
       check_db(req.params[0], shortUrl, db, function(err, doc){
          if(err) return res.json(err);
             res.json(doc);
       });
   });
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.