为什么这段代码有效? (NodeJS / Express异步回调)

时间:2017-08-05 16:55:10

标签: node.js express

这段代码是否尊重NodeJS的异步处理方式?它完全正常工作,但我不明白为什么res.renderDatabase.findOne回调中,而不在Database.aggregate回调中,即使我是{m}使用Database.findOneDatabase.aggregate的结果。

但是,如果我将res.render放在Database.aggregateDatabase.findOne之外,但仍在router.get回调中,则代码无法正常工作在所有(编辑:当然,在这种情况下,我声明数据库查询之外的变量)。怎么会? NodeJS的正确做法是什么?

由于

var _         = require('lodash');
var express   = require('express');
var Database = require('../models/database');
var router    = express.Router();
router.get('/:XXXX', function(req, res, next) {
var XXXX = req.params.XXXX;

var aggregationResults;
Database.aggregate([
    // pipeline
], function(err, results) {
    aggregationResults = results;
    if (err) return next(err);
});

Database.findOne({XXXX: XXXX}, function(err, XXXXresult){
    if(err) return next(err);

    res.render('page', {XXXXresult: XXXXresult, aggregationObject : aggregationResults[0]});


});

});

2 个答案:

答案 0 :(得分:2)

在您的代码中,您调用两个异步数据库函数并将回调传递给每个函数。在这里,您首先调用Database.aggregate()并传递回调,该回调将结果存储在变量即aggregationResults中。然后你正在调用Database.findOne()并将回调传递给它,它会发送两个调用的结果。

这是有效的,因为您的第一个数据库调用(Database.aggregate())在第二个数据库调用之前解析(即Database.findOne())。因此,在您的第二个数据库回调中,您假设第一个数据库调用的结果可用。

但总有可能并非如此。例如,第一个数据库可能会失败,第二个数据库可能会通过。在这种情况下,没有第一个数据库的结果。

NodeJS的正确做法是什么?

早些时候它曾经是一个嵌套的回调,但今天你应该使用promise。您的代码将看起来像这样:

Database.aggregate([pipeline]).then(function(results) {
    aggregationResults = results;
    return Database.findOne({XXXX: XXXX});
}).then(function(XXXXresult){
    res.render('page', {XXXXresult: XXXXresult, aggregationObject : aggregationResults[0]});
});

您可以使用bluebird之类的库来宣传您的数据库方法。

答案 1 :(得分:1)

如果您将res.render移到findOne回调之外,它将在回调之前执行 - 您将无法获得任何数据。毕竟,在路由器(req, res, next)回调之后,这些数据库操作将完成

在您当前的代码中,假设aggregatefindOne函数是异步的,您就会遇到竞争条件。如果findOne回调在aggregate回调之前执行,那么TypeError将会aggregationResults未定义findOne

解决此问题的最简单方法是将aggregate调用放入Promise.all回调中。但这实际上并不是一个好主意,因为这两个函数将一个接一个地执行,这比同时执行它们要慢。

你应该做的是使用Promises,并使用import android.support.v4.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.google.android.youtube.player.YouTubeBaseActivity; import com.google.android.youtube.player.YouTubeInitializationResult; import com.google.android.youtube.player.YouTubePlayer; import com.google.android.youtube.player.YouTubePlayerView; public class ItemTwoFragment extends Fragment { public Player player; public static ItemTwoFragment newInstance(){ ItemTwoFragment fragment=new ItemTwoFragment(); return fragment; } @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState){ View view=inflater.inflate(R.layout.fragment2,container,false); player=new Player(view); return view; } } class Player extends YouTubeBaseActivity{ YouTubePlayerView player; YouTubePlayer.OnInitializedListener onInitializedListener; public Player(View view) { player=(YouTubePlayerView) view.findViewById(R.id.player); onInitializedListener=new YouTubePlayer.OnInitializedListener() { @Override public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer youTubePlayer, boolean b) { youTubePlayer.loadPlaylist("ANak509iCl4&list=PLj9JLcynHDZ6Ymoz8GEWy0U9RDtRM4P_2"); } @Override public void onInitializationFailure(YouTubePlayer.Provider provider, YouTubeInitializationResult youTubeInitializationResult) { } }; player.initialize("AIzaSyCYuQjVhqjVA_I2tPMa-Egy4x-d7KFVFDQ",onInitializedListener); } } 和2个promises之类的东西在两个操作完成后发送响应。