0

I am trying to get mongodb instance synchronously. I know this is not recommended but I just experiment and wonder why this doesn't work. this.db is still undefined after 10 seconds of waiting when normally asynchronous code gets it in less than 500 milliseconds.

Repository.js:

var mongodb = require('mongodb');
var config = require('../config/config');

var mongoConfig = config.mongodb;
var mongoClient = mongodb.MongoClient;

class Repository {
    constructor() {
        (async () => {
            this.db = await mongoClient.connect(mongoConfig.host);
        })();
    }

    _getDb(t) {
        t = t || 500;
        if(!this.db && t < 10000) {
            sleep(t);
            t += 500;
            this._getDb(t);
        } else {
            return this.db;
        }
    }

    collection(collectionName) {
        return this._getDb().collection(collectionName);
    }
}

function sleep(ms) {
    console.log('sleeping for ' + ms + ' ms');
    var t = new Date().getTime();
    while (t + ms >= new Date().getTime()) {}
}

module.exports = Repository;

app.js:

require('../babelize');

var Repository = require('../lib/Repository');
var collection = new Repository().collection('products');
jstice4all
  • 1,878
  • 4
  • 22
  • 33
  • JS is single threaded, the existence of your busy-loop by definition prevents the value from being set. If JS spends it's whole time looping, it has no chance to assign `db`. You'd be better off rephrasing this question as "how should I rewrite this code so it works". – loganfsmyth May 07 '16 at 20:03
  • I think the problem should be represened in question's title at least partially. `This code` requires a reader to peek into the body of the question. – jstice4all May 07 '16 at 20:06
  • 1
    In a nutshell, you CANNOT return an async value from your synchronous function. You simply cannot do that. Your busy wait loop won't work the way Javascript runs. Because you never allow the event loop to process the next event, your database completion callback will never get called. You HAVE to program for an async result. Return a promise or pass in a callback. – jfriend00 May 07 '16 at 20:31
  • Probably should just read this [How do I return a value from an asynchronous function](http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call). All the design options are covered there. – jfriend00 May 07 '16 at 20:46

1 Answers1

2

Javascript is an event-based architecture. All code is initiated via an event from the event queue and the next event is pulled from the event queue ONLY when the code from the previous event has finished executing. This means that your main Javascript code is single threaded.

As such, when you fire an async operation, it starts up an operation and when that operation finishes, it puts an event in the event queue. That event (which will trigger the callback for the async operation) will not run until the code from the previous event finishes running and returns back to the system.

So, now to your code. You start running some code which launches an async operation. Then, you loop forever and never return back to the system. Because of that, the next event in the event queue from the completion of your async operation can NEVER run.

So, in a nutshell, you cannot spin in a loop waiting for an async operation to complete. That's incompatible with the event driven scheme that Javascript uses. You never return back to the event system to let the async completion callback ever run. So, you just have a deadlock or infinite loop.

Instead, you need to code for an async response by returning a promise or by passing in a callback that is called sometime later. And your code needs to finish executing and then let the callback get called sometime in the future. No spinning in loops in Javascript waiting for something else to run.

You can see the async coding options here: How do I return the response from an asynchronous call?

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979