【问题标题】:JavaScript Autoloader: How To Recover From ErrorsJavaScript 自动加载器:如何从错误中恢复
【发布时间】:2013-11-16 22:41:00
【问题描述】:

我有一个适用于 JavaScript 自动加载器的概念验证,但它目前存在一个重大缺陷:它需要重新执行整个代码,而不是简单地从失败的行再次尝试。

这是原型:

<!doctype html>
<html>
<head>
</head>
<body>
<script type="text/javascript">
var app = function(){
    console.log('Initialize App');
    var test = new Test();
    var foo = new Foo();
    var bar = new Bar();
};

var autoload = function(app){

    var recover = function(error){
        var name = error.message.split(' ')[0];
        console.log('Loading '+name);
        //A file could be synchronously loaded here instead
        this[name] = function(){
            console.log(name+' has been dynamically created');
        };
        load(app);
    };

    var load = function(app){
        try {
            app();
        } catch (error){
            if (error.name == "ReferenceError"){
                console.log(error.message);
                recover(error, app);
            }
        }
    };

    load(app);

};

autoload(app);
</script>
</body>
</html>

应该如何工作

这个想法是您的所有应用程序代码都将在app 函数中执行。最后,如果我能让它正常工作,您还可以使用 app 函数将依赖关系映射传递给自动加载器,以便在未定义函数时同步加载依赖关系。依赖映射只是一个对象映射函数名到文件名。

目前的运作方式

如果你不想尝试,上面的代码会向控制台输出以下内容:

Initialize App 
Test is not defined 
Loading Test 
Initialize App 
Test has been dynamically created
Foo is not defined 
Loading Foo 
Initialize App 
Test has been dynamically created
Foo has been dynamically created
Bar is not defined 
Loading Bar 
Initialize App 
Test has been dynamically created
Foo has been dynamically created
Bar has been dynamically created

每次自动加载器捕获错误时,都会重新执行完整的 app 函数。显然,由于多种原因,这并不理想。

从错误中恢复

要进行下一步以完成这项工作,我需要找到一种方法来从错误中恢复,而无需重新执行整个 app 函数。 catch 块中的 error 对象确实提供了发生错误的行号和文件名,但到目前为止,我还没有找到利用该信息的方法。我能想到的一般方法有以下三种:

  1. 在给定行重新启动脚本执行
  2. 从头开始重新执行脚本,但跳过所有行,直到给定行
  3. 将文件作为字符串抓取,按行号将其拆分为数组,其余行为eval

很遗憾,我无法找到有关前两种方法的任何一种的信息。在这三个中,#1 似乎更理想,但我当然也愿意接受其他创造性的建议。据我所知,JavaScript 没有提供在任意行号开始执行脚本的方法。 #3 可能工作,但我不确定它是否会非常高效。我能想到的唯一方法是每次都需要一个额外的请求来将文件文本加载到字符串中。

问题

这无疑是在推动如何在 JavaScript 中加载依赖项的界限。我什至不确定这是否可能,因为我不知道 JavaScript 是否允许这种类型的错误恢复。也就是说,我有兴趣进一步探索它,直到我发现它绝对不可能。

为了使它正常工作:

  1. 有没有办法在 JavaScript 的任意行开始执行脚本?
  2. 还有其他可能更有效的方法吗?尽情发挥创意!

退后一步看大局(假设我能做到这一点):

  1. 人们甚至想要 JavaScript 自动加载器吗?
  2. 与 AMD 这样的自动加载器相比,这样的自动加载器有哪些优点/缺点?
  3. 这种方法会遇到什么样的性能问题?性能受到的影响是否太大而值得?

【问题讨论】:

  • 自动加载这样的东西对我来说似乎有点太“神奇”了。除此之外,我确实认为你将很难让它工作。我不知道有什么方法可以从错误中恢复并从那时起继续运行,虽然您提供了一些您想到的选项,但我认为他们无法与范围界定和关闭进行体面的交互-over 变量。如果您想要这种神奇的方法,我认为使用带有静态分析的预处理器会取得更大的成功。
  • 感谢@icktoofay 的评论。我同意,这有点“神奇”。关于范围界定等的好点。当然,如果没有从错误中恢复的好方法,那么这些都无关紧要。在这一点上,我只是在探索。完全有可能用 JavaScript 做不到。
  • @icktoofay recover from an error and keep running from that point 堆叠承诺适用于异步任务。有一个函数循环遍历对象数组,函数返回一个承诺并将它们堆叠起来。失败时调用恢复(如果有)并使用失败的索引递归调用自身。

标签: javascript error-handling amd autoload


【解决方案1】:

制作起来有点复杂,投掷和捕捉也有点贵。您可以使用 typeof window["Test"]!=="function" 然后创建它,而不是像这样使用 try catch。

但对于一般的恢复和继续方法,以下代码可以解决问题。

var app = (function(i){
  var objects=new Array(3),
  fnNames=["Test","Foo","Bar"];
  return function(){
    var len=fnNames.length
     ,test,foo,bar;
    //check if closure vars have been reset, if so
    // this has ran successfully once so don't do anything?
    if(objects===false){
      console.log("nothing to do, initialized already");
      return;
    }
    while(i<len){
      try{
        objects[i] = new window[fnNames[i]]();
        i++;
      }catch(e){
        if (e.name == "TypeError"){//different syntax different error
          throw {"fnName":fnNames[i]};
        }
      }
    }
    //store instances in the variables
    test = objects[0];
    foo = objects[1];
    bar = objects[2];
    //reset closure vars assuming you only call app once
    //  when it's successful
    i=null;objects=false;
    console.log("Got all the instances",test,foo,bar);
  };
}(0));

var autoload = function(app){
  var recover = function(name){
    console.log('Loading '+name);
    //A file could be synchronously loaded here instead
    this[name] = function(){
      this.name=name;
      console.log(this.name+' has been dynamically created');
    };
    load(app);
  };

  var load = function(app){
    try {
      app();
    } catch (error){
      //if statement here no longer needed
      // object thrown has fnName (function name)
      recover(error.fnName, app);
    }
  };
  load(app);
};

autoload(app);
autoload(app);

【讨论】:

    猜你喜欢
    • 2020-01-15
    • 2023-03-07
    • 2020-11-09
    • 1970-01-01
    • 2021-06-25
    • 1970-01-01
    • 2017-07-27
    • 2017-03-19
    • 2016-06-19
    相关资源
    最近更新 更多