我正在尝试用 Meteor.wrapAsync 包装超级代理 NPM,一切正常,直到下面代码的最后一行,这导致我的 meteor 应用程序崩溃。
var superagent = Meteor.npmRequire('superagent');
// Example of how superagent works
superagent.get('http://127.0.0.1:8080/json/', function(result){
console.log(result); // Works, shows the result
});
// This appears to work too
var agentAsync = Meteor.wrapAsync(superagent.get);
// This crashes app
agentAsync('http://127.0.0.1:8080/json/');
我也尝试将上下文传递给wrapAsync(),它没有区别:
var agentAsync = Meteor.wrapAsync(superagent.get, superagent);
以下是控制台输出:
W20141124-17:31:32.094(0)? (STDERR)
W20141124-17:31:32.136(0)? (STDERR) /home/ciwolsey/.meteor/packages/meteor-tool/.1.0.35.1bjny7b++os.linux.x86_64+web.browser+web.cordova/meteor-tool-os.linux.x86_64/dev_bundle/lib/node_modules/fibers/future.js:206
W20141124-17:31:32.136(0)? (STDERR) throw(ex);
W20141124-17:31:32.137(0)? (STDERR) ^
W20141124-17:31:32.137(0)? (STDERR) [object Object]
W20141124-17:31:32.137(0)? (STDERR) at Object.Future.wait (/home/ciwolsey/.meteor/packages/meteor-tool/.1.0.35.1bjny7b++os.linux.x86_64+web.browser+web.cordova/meteor-tool-os.linux.x86_64/dev_bundle/lib/node_modules/fibers/future.js:326:15)
W20141124-17:31:32.137(0)? (STDERR) at packages/meteor/helpers.js:118
W20141124-17:31:32.137(0)? (STDERR) at app/server/main.js:5:1
W20141124-17:31:32.137(0)? (STDERR) at app/server/main.js:8:3
W20141124-17:31:32.137(0)? (STDERR) at /home/ciwolsey/projects/hello/.meteor/local/build/programs/server/boot.js:168:10
W20141124-17:31:32.138(0)? (STDERR) at Array.forEach (native)
W20141124-17:31:32.138(0)? (STDERR) at Function._.each._.forEach (/home/ciwolsey/.meteor/packages/meteor-tool/.1.0.35.1bjny7b++os.linux.x86_64+web.browser+web.cordova/meteor-tool-os.linux.x86_64/dev_bundle/lib/node_modules/underscore/underscore.js:79:11)
W20141124-17:31:32.138(0)? (STDERR) at /home/ciwolsey/projects/hello/.meteor/local/build/programs/server/boot.js:82:5
=> Exited with code: 8
这是Meteor.wrapAsync
的源和superget.get
的源
Meteor.wrapAsync
基本上是围绕Meteor.bindEnviroment
的薄包装。它提供了一个等待Fiber
的绑定函数。
superget.get
最终尝试调用传递给它的回调函数,Request.prototype.callback
有趣的是,Meteor.bindEnvironment
采用Fibers.resolver
函数(它接受两个参数),并将其包装在一个不带参数的函数中。
因此,当Request.prototype.callback
尝试查看fn.length
以查看它是否应该用(err, res)
调用它或用emit
发送错误时......它做后者..
为了使这项工作,我们需要短路Request.prototype.callback
,并使其认为没有参数的函数可以调用为fn(err, res)
superget.Request.prototype.callback = function(err, res){
var fn = this._callback;
if (2 == fn.length || 0 == fn.length) return fn(err, res);
if (err) return this.emit('error', err);
fn(res);
};
或者,您可以编写自己的Meteor.wrapAsync
,以提供具有正确函数长度的回调。 例如:
function wrapAsync(fn, context) {
//XXX Shortened version of wrapAsync. Only works on server, doesn't allow for callback to be passed.
return function (/* arguments */) {
var self = context || this;
var newArgs = _.toArray(arguments);
var fut = new Future();
var callback = Meteor.bindEnvironment(fut.resolver());
newArgs.push(function(err, res){
return callback.apply(this, arguments);
});
fn.apply(self, newArgs);
return fut.wait()
};
}
Meteor.wrapAsync
采用第二个参数,这是应该调用包装函数的上下文(以保留this
正确的值)。
请尝试改用以下语法:
var agentAsync = Meteor.wrapAsync(superagent.get, superagent);
如果未传递正确的上下文,调用将使应用崩溃,因为它无法提取this
属性,就像直接调用superagent.get
时通常应该提取的那样。
你试过使用Future吗?下面是一个示例
Meteor.methods({
syncMethod: function() {
// load Future
Future = Npm.require('fibers/future');
var theFuture = new Future();
// call the function and store its result
TheAsyncFuncWeWantItToWait("foo", function (error,results){
if(error){
theFuture.throw(error);
}else{
theFuture.return(results);
}
});
return theFuture.wait();
}
});
这是一个古老的问题,但我在这里没有看到明确的答案,所以我想分享对我有用的方法。
const request = superagent
.post(`${basePath}/api/xxx`)
.set('Content-Type', 'application/json')
.send({ fileReference });
const response = Meteor.wrapAsync(request.end, request)();
由于request.end()
是需要回调的函数,因此这是您要传递到Meteor.wrapAsync的内容。并且你必须将回调绑定到原始请求,否则它在全局上下文中运行(但它需要在原始请求的上下文中运行)。
希望这对其他人有所帮助!