链接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中查看这个问题来讨论实现,这个实现也有一个链接,用来实现它的变化,你可以看到它是如何工作的。