我可以在节点中的 q promises 中创建全局变量吗?

Can I create global variables in q promises in node?

在(尝试)切换到 promises 之前,我可以在嵌套函数的整个范围内拥有变量。例如,我可以从数据库中获取一个 user,而不是立即使用它,然后在嵌套在链中很远的函数中使用它。

那么,有了 promises,我必须明确地将所需的变量传递给下一个函数吗?或者有其他方法可以解决这个问题吗?

目前我的代码看起来像这样

exports.add = function ( req, res ) {
    var defer = q.defer()

    // check sign in and get user information as 'user' 

    function getUser ( req, res ) {
        User.findOne( { token: req.token }, function ( err, user ) {
            if ( err || !user )
                return res.json( {
                    status: "error",
                    message: "Error authenticating: " + err
                })

            return user
        })
    }

... // more functions here

defer.promise
.then( getUser )
.then( youtubeAuthenticate )
.then( getVideoId )
.then( youtubeFind )
.then( makeVideoObject )

defer.resolve( req, res )
}

问题是我无法在 makeVideoObject.

等函数中访问 user

编辑:另一件真正让我难过的事情是我的一些函数如何可以 return 一个值,比如 videoId,这似乎被我的最终函数识别,makeVideoObject,但其他人不是,它们之间没有真正的区别。保留在范围内,其他变为 undefined.


EDIT2:我的变量 videoId 是 "global" 的原因是因为我想我是在像 videoId = ... 那样声明它。所以不是 return videoId 使它成为全球性的,而是声明它的方式。我这样做是徒劳的,

function getUserData () {
    user = getUser()
    videoId = getVideoId()
    youtubeAuthenticate()
}

function getVideoData () {
    vData = youtubeFind()
}   

defer.promise
.then( getUserData )
.then( getVideoData )
.then( function (user, videoId, vData ) {
    makeVideoObject( user, videoId, vData )
})

defer.resolve()

但没有快乐。一切依旧undefined.


EDIT3:我已经尝试了下面的回答以及我能想到的任何排列。这是我目前拥有的。

var user = getUser()
var videoId = getVideoId()
var video = q(videoId).then( youtubeFind )

q.all( [ user, video ] ).then( function( results ) {
    if ( !results[0] )
        return res.json( "Not authenticated." )

    makeVideoObject( results[0], results[1] )
})

使用调试器,两个结果都未定义。在 getUser()youtubeFind() 函数中使用 console.log,我发现它们有效。例如,这里是 getUser() 函数。

function getUser () {
    User.findOne( { token: req.token }, function ( err, user ) {
        return user
    })
}

另请注意,我通过将 authenticateYoutube() 内容直接添加到 youtubeFind() 中来简化事情。


回答:出于礼貌,我可能会选择下面的答案,但这是对Node和JS的一系列误解导致了这个问题。这并没有回答提出的问题,但这是我做错的地方。

首先,我认为 return 放在嵌套函数(闭包)中时应该是嵌套在其中的 "root" 函数的 return <VALUE>。实际上,它会只是它所在的直接函数的 return 值。这里可能是 JS 101。似乎也无法从嵌套函数中影响在父函数中声明的变量。

其次,您需要以某种方式实际构建一个函数,使其像 promise 一样工作。那个格式是这样的:

function getUser () {
    var defer = Q.defer()   // create the promise object
    User.findOne( { token: req.token }, function ( err, user ) {
        defer.resolve( user )   // add the return value to the object
    })

    return defer.promise    // return the object, with promise and value
}

如果你想 运行 几个不相互依赖的任务,然后 运行 一个需要这些值的任务,你可以这样做:

Q.all([getUserData(), getVideoData()]).spread(function(user, video) {
   // user and video are available
   makeVideoObject(user, video)
});

all 获取可以并行获取的承诺列表; spread 将那些 return 值拆分为单独的参数,然后可以在回调中使用这些参数。那是你想要做的吗?

正如本杰明格鲁恩鲍姆所说 You're Missing the Point of Promises

Promises 不仅仅是将回调串联起来的实用程序。它们是未来值的占位符。

因此,如果您想保留步骤列表中途某处的结果,只需抓住它:

var user = getUser();

user.then(doSomethingWithUser);
user.then(doSomethingElseWithUser);

您还没有向我们展示您的函数的实际作用,因此我将尽最大可能向您展示如何收集所需的部分然后重新组合它们。我不确定 "q" 你指的是 Q, or to $q 还是其他完全不同的东西,所以我假设至少有一些方便的方法可用:

// All variables declared with "var" below are promises

var user = getUser();
var videoId = user.then(getVideoId); // assumes getVideoId depends on user
var youTubeAuth = user.then(youTubeAuthenticate);

var videoData = q.all([youTubeAuth, videoId]).then(function (values) {
    return getVideoData(values[0], values[1]);
});

var videoObject = q.all([user, videoId, videoData]).then(function (values) {
    return makeVideoObject(values[0], values[1], values[2]);
});