如何在Node.js中创建异步回调?

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);
   });

下面的res.json语句会在其他函数返回值之前运行并返回一个未定义的变量。
   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){

在执行下面的语句并返回值之前,将会执行 res.json 语句。

            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();
    });
}

在上述代码中,res.json语句在GET请求下方定义的函数有机会完成执行之前就被执行了,结果是res.json返回一个未定义的变量。我知道我必须在我的应用程序中实现异步功能(可能是promises?),但我完全不知道如何做!
2个回答

1
回调函数就是一个函数调用中的参数。
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();

你对#googleUrl.shorten()的调用紧接着对#res.json()的调用,就像对#foo()的调用紧接着对#bar()的调用一样。
你的回调函数:
function(err, shortUrl){
   if(err){
       console.log(err);
   }else{
       console.log(shortUrl);
   }
   doc = check_db(req.params[0], shortUrl, db);
}

该函数是异步执行的,意味着它不会中断正常的控制流程。当您需要推迟像#res.json(doc)这样的语句的执行时,您必须确保它的执行也被从正常的控制流程中取出。为此,您需要在#check_db()#save_db()中都接受一个回调参数。新的函数签名将如下所示:

function save_db(longUrl, shortUrl, db, callback)

并且

function check_db(longUrl, shortUrl, db, callback)

然后将一个回调函数传递给你的db函数,这个函数接受一个参数'doc',并执行res.json(doc)。例如:

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

注意:你只需要从#check_db()向#save_db()传递“callback”即可。
最后,在异步函数执行完成后,请调用回调函数而不是返回值。例如:
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);
    }
});

我已经把对 #save_db() 的更改留给你了。祝你好运!


0

check_dbsave_db改为异步

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)});
    });
}

使用方法如下:

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);
       });
   });
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接