我很乐意编写一个带有函数的通用包装器,并返回该函数的“async-style”版本 IF 它开始时不是异步。
麻烦的是,没有简单的方法可以知道呼叫是同步还是异步。所以...这基本上“无法完成”。对?
(请注意,包装器应将同步函数协调为异步样式,并单独使用LEAVE异步函数)
var wrapper = function( fn ){
return function(){
var args = Array.prototype.splice.call(arguments, 0);
var cb = args[ args.length - 1 ];
// ?!?!?!?!?
// I cannot actually tell if `fn` is sync
// or async, and cannot determine it!
console.log( fn.toString() );
}
}
var f1Async = wrapper( function( arg, next ){
next( null, 'async' + arg );
})
var f2Sync = wrapper( function( arg ){
return 'sync' + arg;
})
f1Async( "some", function(err, ret ){
console.log( ret );
});
f2Sync( "some other", function(err, ret ){
console.log( ret );
});
答案 0 :(得分:2)
您无法找到函数的可接受参数,因此您无法确定是否需要回调。
答案 1 :(得分:1)
根本无法做到。故事结束。
答案 2 :(得分:1)
在javascript中,没有办法检查函数的最后一个参数是否是函数,因为在javascript中你没有定义参数的类型。
我的解决方案通过获取函数中的参数列表,然后使用RegExp查看该参数是否用作函数来工作。此外,如果没有直接使用回调(比如将其传递给其他东西),它会有一个参数名称列表被视为回调。
代码是:
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var CALLBACK_NAMES = [ "next", "done", "callback", "cb"];
function getParamNames(func) {
var fnStr = func.toString().replace(STRIP_COMMENTS, '')
var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(/([^\s,]+)/g)
if(result === null)
result = []
return result
}
function checkIfParamIsFunction(func, paramName){
var fnStr = func.toString();
if (fnStr.replace(new RegExp("(" + paramName + "\s*\([A-Za-z0-9,\.]*\)?!{|" + paramName + ".apply\([A-Za-z0-9,\.]*\)|" + paramName + ".call\([A-Za-z0-9,\.]*\))", ""), "{<._|/}") != fnStr) { // Remove All Calls to the arg as a function, then check to see if anything was changed.
return true;
} else {
return false;
}
}
function makeAsync(func) {
var paramNames = getParamNames(func);
if (checkIfParamIsFunction(func, paramNames[paramNames.length - 1])
|| CALLBACK_NAMES.indexOf(paramNames[paramNames.length - 1]) != -1) {
// Function Is Already async
return func;
} else {
return function () {
var args = Array.prototype.slice.call(arguments);
var cb = args.pop();
cb(func.apply(this, args));
}
}
}
function test1(a){
return (a+' test1');
};
function test2(a, callback){
return callback(a+' test2')
};
function main(){
var tested1 = makeAsync(test1);
var tested2 = makeAsync(test2);
tested1('hello', function(output){
console.log('holy shit it\'s now async!!');
console.log(output);
});
tested2('world', function(output){
console.log('this one was already async tho');
console.log(output);
});
}
main();
只需调用makeAsync(function)
即可返回异步功能。如果您使用function.apply
或.call
。
答案 3 :(得分:1)
尽管如此,这不是答案,而是一个很好的选择。我提供了基于浏览器的JavaScript示例,但同样的类也可以在Node上使用。
为了解决这个问题,开发了许诺。但是我们使用修改后的promise版本如下。
function AtomPromise(f)
{
// Success callbacks
this.sc = [];
// Error callbacks
this.ec = [];
this.i = f;
this.state = 'ready';
}
AtomPromise.prototype ={
finish: function(s,r) {
this.result = r;
var c = s ? this.sc : this.ec;
this.state = s ? 'done' : 'failed' ;
for(var i=o ; i< c.length; i++){
c[i](this);
}
},
invoke: function(f) {
this.state = 'invoking';
this.i(this);
},
then: function(f) {
this.sc.push(f);
},
failed: function(f){
this.ec.push(f);
},
value: function(v) {
if(v !== undefined ) this.result = v;
return this.result;
},
pushValue: function(v) {
var _this = this;
setTimeout(100, function () {
_this.finish(true, v);
});
}
}
//wrap jQuery AJAX
AtomPromise.ajax = function( url, options ) {
return new AtomPromise(function (ap){
$.ajax(url, options)
.then( function(r){ ap.finish(true, r); })
.failed( function (){ ap.finish( false, arguments) });
}) ;
}
//Wrape sync function
AtomPromise.localStorage = function( key ) {
return new AtomPromise(function (ap){
var v = localStorage[key];
ap.pushValue(v);
}) ;
}
// Calling Sequence
AtomPromise.ajax( 'Url' ).then( function(ap) {
alert(ap.value());
}).invoke();
AtomPromise.localStorage( 'Url' ).then( function(ap) {
alert(ap.value());
}).invoke();
这两个函数现在都是异步的。 Push Value方法通过setTimeout生成结果路由,使进一步的调用异步。
这在Web Atoms JS中用于将异步代码擦除到单个属性中,并且通过遵循一种模式,您可以摆脱异步回调地狱。 http://webatomsjs.neurospeech.com/docs/#page=concepts%2Fatom-promise.html
免责声明:我是Web Atoms JS的作者。