简体   繁体   中英

Wait for asynchronous function to finish without adding callback

I'm writing tests for my Node.js/Express/Mongoose project using Mocha and Should.js, and I'm testing out my functions that access my MongoDB. I'm want these tests to be completely independent from the actual records in my database, so I want to create an entry and then load it, and do all my tests on it, then delete it. I have my actual functions written (I'm writing tests after the entire project is complete) such that the create function does not have a callback; it simply just renders a page when it's done. In my tests script, I call my load_entry function after I call create , but sometimes create takes longer than usual and thus load_entry throws an error when it cannot actually load the article since it has yet to be created. Is there any way to make sure an asynchronous function is finished without using callbacks?

Please let me know if there is any more info I can provide. I looked all over Google and couldn't find anything that really answered my question, since most solutions just say "use a callback!"

Use what is known as a promise

You can read more about it here .

There are lots of great libraries that can do this for you.

Q.js is one I personally like and it's widely used nowadays. Promises also exist in jQuery among many others.

Here's an example of using aq promise with an asynchronous json-p call: DEMO

var time;
$.ajax({
    dataType: 'jsonp',
    type: 'GET',
    url: "http://www.timeapi.org/utc/now.json",
    success: function (data) {
        time = data;
    },
    error: function (data) {
        console.log("failed");
    }
})
.then(function(){ // use a promise library to make sure we synchronize off the jsonp
    console.log(time);    
});

This is definitely the kind of thing you want a callback for. Barring that, you're going to have to write some kind of callback wrapper that polls the database to determine when it has finished creating the relevant records, and then emits an event or does some other async thing to allow the test to continue.

Since the only native way to do asynchronous things are: setTimeout , setInterval and addEventListener , and they all take a callback you will eventually have to use a callback somewhere.

However, you can hide that by using Promises/A, also known as Deferreds.

Your code could look like this:

db.create_entry("foo", data).done(function (entry) {
    db.delete_entry(entry).done(function () {
        console.log("entry is deleted");
    });
});

Using then-chaining:

db.create_entry("foo", data).then(function (entry) {
    return db.delete_entry(entry);
}).done(function () {
    console.log("entry is deleted");
});;

I found a solution that works. What I did was to add a callback to my function ( next ) and only call it if it's specified (ie, for the tests):

//more stuff above
article.save(function(err){
    if (!err) {
        console.log(req.user.username + ' wrote ' + article.slug)
        return next() || res.redirect('/admin')
    }
    return next(err) || res.render('articles/entry_form', {
        title: 'New Entry',
        article: article,
    })
})

This way, when I run the actual server and no callback is specified, it won't throw an error, because it will simply return the res.render statement.

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