5

我很想编写一个通用包装器,它接受一个函数,并返回该函数的“异步样式”版本,如果它不是异步开始的。

麻烦的是,没有简单的方法可以知道调用是同步的还是异步的。所以......这基本上是“做不到”。对?

(请注意,包装器应将同步函数与异步样式协调一致,并单独保留异步函数)

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 );
});
4

4 回答 4

2

您无法找出函数接受的参数是什么,因此您无法确定它是否需要回调。

于 2013-11-04T12:58:53.930 回答
1

根本做不到。故事结局。

于 2013-10-31T05:29:15.997 回答
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

于 2013-11-09T01:43:40.007 回答
1

虽然,这不是答案,而是一个不错的选择。我提供了基于浏览器的 JavaScript 示例,但同样的类也可以在 Node 上使用。

为了解决这个问题,promise 应运而生。但是,我们使用如下的修改版本的 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 的作者。

于 2013-11-09T05:32:14.817 回答