2016-01-04 52 views
7

我们用我们的项目require.js,我们需要重写的setTimeout在行705,这是我们需要忽略/都忽略莫名其妙此的setTimeout(代码我的意思是运行它),如果我改变它在开放源代码显式更改版本时代码将会丢失的问题,我应该如何从外部重写此setTimout只为require.js文件并保持它很长时间当我使用这个库,是否有可能在优雅方式在JS全球?覆盖的setTimeout

https://github.com/jrburke/requirejs/blob/master/require.js

这是行705

 //If still waiting on loads, and the waiting load is something 
     //other than a plugin resource, or there are still outstanding 
     //scripts, then just try back later. 
     if ((!expired || usingPathFallback) && stillLoading) { 
      //Something is still waiting to load. Wait for it, but only 
      //if a timeout is not already in effect. 
      if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { 
       checkLoadedTimeoutId = setTimeout(function() { 
        checkLoadedTimeoutId = 0; 
        checkLoaded(); 
       }, 50); 
      } 
     } 

仅供参考,我们做的是 Chrome: timeouts/interval suspended in background tabs?

+0

这戒指我所有的警钟,为什么你需要做的呢?改变全球功能和对象几乎从来都不是一个好主意。 –

+0

@MadaraUchiha - 当我们在非活动选项卡上使用require.js(我的意思是在打开新窗口后),每个服务负载需要一秒钟后,查看我提供给setTimout的链接! –

+0

但是,它处于非活动状态,用户不在观看,您为什么关心需要多长时间? –

回答

5

你已经表明你的目标是解决Chrome在setTimeout上为后台标签执行的限制。我不认为这样做是一个好主意,但如果你愿意的话,那么你肯定应该修补RequireJS而不是在全球范围内搞乱setTimeout。你说:

,如果我在开源代码改变它明确当我改变版本的代码将会丢失

只有当你不使用一个明智的方法来执行的变化这是真的。有理由做到这一点。例如,您可以使用Gulp将require.js文件安装在node_modules中(在安装RequireJS和npm之后),并在build中生成修补文件。然后你在你的应用程序中使用这个补丁文件。这里是gulpfile.js

var gulp = require("gulp"); 

// Bluebird is a good implementation of promises. 
var Promise = require("bluebird"); 

// fs-extra produces a `fs` module with additional functions like 
// `ensureDirAsync` which is used below. 
var fs = require("fs-extra"); 

// Make it so that for each the function in fs that is asynchronous 
// and takes a callback (e.g. `fs.readFile`), a new function that 
// returns promise is created (e.g. `fs.readFileAsync`). 
Promise.promisifyAll(fs); 

var to_replace = 
"if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) {\n\ 
        checkLoadedTimeoutId = setTimeout(function() {\n\ 
         checkLoadedTimeoutId = 0;\n\ 
         checkLoaded();\n\ 
        }, 50);"; 

var replace_with = 
"if (isBrowser || isWebWorker) {\n\ 
        checkLoaded();"; 


gulp.task("default", function() { 
    // Use `fs.ensureDirAsync` to make sure the build directory 
    // exists. 
    return fs.ensureDirAsync("build").then(function() { 
     return fs.readFileAsync("node_modules/requirejs/require.js") 
      .then(function (data) { 
       data = data.toString(); 

       // We use the split/join idiom to a) check that we get 
       // the string to be replaced exactly once and b) 
       // replace it. First split... 
       var chunks = data.split(to_replace); 

       // Here we check that the results of splitting the 
       // chunk is what we expect. 
       if (chunks.length < 2) { 
        throw new Error("did not find the pattern"); 
       } 
       else if (chunks.length > 2) { 
        throw new Error("found the pattern more than once"); 
       } 

       // We found exactly one instance of the text to 
       // replace, go ahead. So join... 
       return fs.writeFileAsync("build/require.js", 
             chunks.join(replace_with)); 
      }); 
    }); 
}); 

你需要运行它之前已经运行npm install gulp fs-extra bluebird requirejs。无论如何,你可以使用Gulp,你可以使用Grunt,或者你可以使用任何其他想要执行构建的系统。要点如下:

  1. 您有一个可重复的自动方法来修补RequireJS。如果您使用npm安装了RequireJS的新版本,则在重建软件时,只要RequireJS的代码不会以阻止应用该补丁的方式进行更改,该补丁就会自动应用。如果更改阻止应用修补程序,请参阅下一个要点。

  2. 此方法在运行时比覆盖setTimeout更强大。假设James Burke在新版本的RequireJS中决定将checkLoaded重命名为checkDone并重命名相关变量(以使checkLoadedTimeoutId变成checkDoneTimeoutId)。上面的gulp文件在再次运行时会引发异常,因为它找不到要替换的文本。您必须更新要替换的文本和替换文件,以便修补程序可以与新版本的RequireJS一起使用。这样做的好处是,您会收到事先已经发生变化的警告,并且需要查看该补丁。 也许在您将软件的新版本交付给客户之后,您在游戏的后期中不会有惊喜。

    在运行时覆盖setTimeout的方法只会默默无法完成工作。他们将会寻找一个包含checkLoadedTimeoutId的函数,这个函数在新版本中不再存在。所以他们只会让RequireJS按默认的方式行事。失败将是一个微妙的。 (我已经运行RequireJS与该向上的50个模块加载未优化当一个项目的setTimeout所提出的定制版本。我看到用股票setTimeout和使用自定义setTimeout RequireJS RequireJS之间没有明显的区别。)

  3. 此方法不会减慢每个使用setTimeoutsetTimeout被RequireJS以外的其他代码使用。无论你如何削减它,在自定义替换中添加代码到setTimeout,开始寻找传递给它的每个函数中的字符串将使全部使用setTimeout变慢。

+0

优雅的方法!它不需要编辑文件并更改setTimeout本身......只需要在项目中管理一个或多个gulp/grunt/other任务。 –

1

如果你真的需要这...尝试添加加载requirejs前的原因:

function isTimeoutIgnoredFunction(fn, time) { 
    // you should configure this condition for safety, to exclude ignoring different timeOut calls! 
    return time == 50 && fn.toString().indexOf('checkLoadedTimeoutId') > -1; 
} 

window.setTimeoutOriginal = window.setTimeout; 

window.setTimeout = function(fn, time) { 
    if (isTimeoutIgnoredFunction(fn, time)) { 
     return fn(); // or return true if you don't need to call this 
    } else { 
     return window.setTimeoutOriginal.apply(this, arguments); 
    } 
}; 

Sho如果没有提供requirejs minify,则在上一个Chrome,Firefox,IE中工作...您需要为您支持的浏览器和缩小的require.js文件重写函数“isTimeoutIgnoredFunction”。

要查看可以使用的字符串:

console.log((function() { 
        checkLoadedTimeoutId = 0; 
        checkLoaded(); 
       }).toString()); 

但在某些浏览器中它可以只是像“功能”。

如果你还没有很多的setTimeout的也可以是合适的解决方案...

2

您可以覆盖setTimeout和检查,如果回调传递函数包含从require.js(checkLoadedTimeoutId)在函数中使用的变量。如果是,立即调用函数,否则调用原函数setTimeout函数。

(function(setTimeoutCopy) { 
    setTimeout = function(fn, timeout) { 
    if (fn.toString().indexOf("checkLoadedTimeoutId") >= 0) { 
     return fn(); 
    } else { 
     return setTimeoutCopy.apply(null, arguments); 
    } 
    }; 
}(setTimeout)); 

请注意,此代码存在多个问题。如果你传递一个函数setTimeout其中包含checkLoadedTimeoutId,它也会立即执行。另外,如果require.js代码被缩小并且变量被重命名,它将不起作用。

总结起来,没有好的办法做到这一点。也许试图找到一种不同的方式来实现你想要的。另外要注意的是,如斑宇智波说:

改变全局函数和对象是几乎从未一个好主意。

1

其他的方式来实现它正在图书馆的Ajax请求,并修补它之前加载库,但你需要装入的方式提出请求(香草JS或jQuery的...)

下一个代码是加载requirejs并在将其加载到DOM之前使用正则表达式对其进行修补的示例。

$(function(){ 
 
    // here you can put the url of your own hosted requirejs or a url from a CDN 
 
    var requirejsUrl = 'https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.22/require.js'; 
 
    
 
    function patch(lib){ 
 
    var toReplace = /if\s*\(\(isBrowser\s*\|\|\s*isWebWorker\)\s*\&\&\s*!checkLoadedTimeoutId\)\s*\{([^{}]|\{[^{}]*\})*\}/; 
 
    var by = 'if (isBrowser || isWebWorker) { checkLoaded();}'; 
 
    return lib.replace(toReplace, by); 
 
    } 
 
    
 
    $.get(requirejsUrl, function(lib){ 
 
    var libpatched = patch(lib); 
 
    
 
    var script=document.createElement('script'); 
 
    script.innerText=libpatched; 
 
    $('body').append(script); 
 
    
 
    console.log(window.require); // requirejs patched is loaded 
 
    }); 
 
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

+0

此答案中的代码异步加载RequireJS,这意味着任何依赖于RequireJS的代码在运行之前都必须等待RequireJS出现。告别简单的''(这是一个相当常见的模式,我碰巧使用它)。 – Louis