链接Q.ninvoke时键入错误

当我试图使用Q连接Node.js中的mongodb函数时出现错误,如下所示:

 Q.ninvoke(MongoClient, 'connect', 'mongodb://127.0.0.1:27017/mydb') .then(function(db){ return Q .ninvoke(db, 'createCollection', 'mycollection') .ninvoke(db.collection('mycollection'), 'createIndex', {id: 1}) // error occurs here .ninvoke(db, 'close') .then(function(){...}) }); 

我得到的错误消息:

 TypeError: Cannot call method 'apply' of undefined at Promise.post (/path/to/my/project/node_modules/q/q.js:1157:36) at Promise.promise.promiseDispatch (/path/to/my/project/node_modules/q/q.js:784:41) at /path/to/my/project/node_modules/q/q.js:600:44 at runSingle (/path/to/my/project/node_modules/q/q.js:133:13) at flush (/path/to/my/project/node_modules/q/q.js:121:13) at process._tickCallback (node.js:442:13) 

根据这个消息,q.js中的第1157行是关于:

 Q.fulfill = fulfill; function fulfill(value) { return Promise({ ... "post": function (name, args) { if (name === null || name === void 0) { return value.apply(void 0, args); } else { return value[name].apply(value, args); // error occurs here: line 1157 } } 

虽然我不知道Q里面发生了什么事情,但我猜db.collection('mycollection')没有被正确地传递给行1157中的value 。我在q的Github上提出了这个问题,但没有得到任何回应。

如果我改变这样的代码,一切正常工作再次:

 Q.ninvoke(MongoClient, 'connect', 'mongodb://127.0.0.1:27017/mydb') .then(function(db){ return Q .ninvoke(db, 'createCollection', 'mycollection') .then(function(){ return Q.ninvoke(db.collection('mycollection'), 'createIndex', {id: 1}) // no error this time .then(function(){ return Q.ninvoke(db, 'close').then(function(){...}); }); }); }); 

然而,这里是一个随着连锁而成长的金字塔。 我认为Q应该像第一个例子那样支持链接ninvoke

总之,我的问题是对Q的使用是否存在误解,还是Q中存在一个bug?

我使用的包版本:node.js:v0.10.36 Q:1.4.0 mongodb:2.0.31

更新

我排除了MongoDB的因素,并缩小了问题的范围:

 var TestClass = function (name){ }; TestClass.prototype.printName = function (callback) { console.log('printName called'); return callback(null); }; var test = new TestClass('test object'); test.printName(function (err) { test.printName(function (err) { console.log('callback called'); }); }); 

在这种情况下,输出应该是:

 $ node q-test.js printName called printName called callback called 

但是如果我使用Q如下:

 Q.ninvoke(test, 'printName') .ninvoke(test, 'printName') .then(function(){ console.log('callback called'); }) .done(); 

原来这样的错误输出:

 $ node test.js printName called /path/to/my/project/node_modules/q/q.js:155 throw e; ^ TypeError: Cannot read property '[object Object]' of undefined at Promise.post (/path/to/my/project/node_modules/q/q.js:1161:29) at Promise.promise.promiseDispatch (/path/to/my/project/node_modules/q/q.js:788:41) at /path/to/my/project/node_modules/q/q.js:556:49 at runSingle (/path/to/my/project/node_modules/q/q.js:137:13) at flush (/path/to/my/project/node_modules/q/q.js:125:13) at process._tickCallback (node.js:442:13) at Function.Module.runMain (module.js:499:11) at startup (node.js:119:16) at node.js:929:3 

TL; DR版本 :当在链中调用而不是直接Q.ninvoke() ,调用函数的对象来自链中前一个函数的结果,而不是从第一个参数到qpromise.ninvoke()调用。

详细说明:虽然我最初一直认为这个问题是在你的原始代码中,但是当在下面的行中调用db.collection('mycollection')时,集合本身还不会被创build。 在您的正确/工作版本中,这将被解决,因为ninvoke绑定直到集合创build之后才会发生。 我现在看到,一般问题是你如何调用.ninvoke()超过它在链中的初始使用。

您更新的示例并不直接有帮助,因为您正在使用ninvoke()但不使用节点样式的callbackforms,所以您肯定会得到不同的错误。

至less要提到你的新的非Mongo例子,根本问题是你如何在随后的调用中使用.ninvoke() 。 在应用到返回的promise后,最初的Q.ninvoke()后续的.ninvoke()调用之后,使用promise的返回值作为第一个参数。 这可能比任何其他更容易说明,请查看我对“printName”示例所做的更改:

 var TestClass = function (name){ }; TestClass.prototype.printName = function (callback) { console.log('printName called'); return callback(null,this); // node style callback, also return this }; var test = new TestClass('test object'); Q.ninvoke(test, 'printName') // in the lines below, the result of the prior line is the "implied" first // parameter to ninvoke(); .ninvoke('printName') .ninvoke('printName') .then(function(){ console.log('callback called'); }) .done(); 

希望这可以说明发生了什么事情。

回到原来的问题,虽然我不是一个Mongo用户,但从查看文档看来,createCollection实际上会返回集合,所以您可能可以修改以前的代码:

 return Q .ninvoke(db, 'createCollection', 'mycollection') .ninvoke(db.collection('mycollection'), 'createIndex', {id: 1}) // error occurs here 

 return Q .ninvoke(db, 'createCollection', 'mycollection') .ninvoke('createIndex', {id: 1}) // error *should not occur* here :-) 

但是,我不知道你会想要做什么关于“closures”,因为大概createIndex不会只是返回一个引用数据库。 我不知道API,但大概你可以使用Q的.get() 或相关的函数来获取它,然后传递给.ninvoke('close')

在Q中查看这个问题来讨论实现,这个实现也有一个链接,用来实现它的变化,你可以看到它是如何工作的。