Ⅰ 加载js时,很容易导致js的堵塞,该怎么处理
无阻塞加载js
浏览器加载静态资源和js的方式都是线性加载,所以一般情况可以将js放到</body>前,防止UI线程的阻塞。
而某些时候我们既希望js在整个网页的头部就加载,又担心js阻塞导致网站加载缓慢,就可以用到无阻塞加载js技术。
Dynamic Script Elements 动态脚本元素
DOM允许我们使用javascript动态创建HTML的几乎所有文档内容,一个新的<script>元素可以非常容易的通过标准DOM创建:
var script = document.createElement ("script"); script.type = "text/javascript"; script.src = "file1.js"; document.body.appendChild(script);
新的<script>元素加载file1.js源文件。此文件当元素添加到页面后立刻开始下载。此技术的重点在于:无论在何处启动下载,文件的下载和运行都不会阻塞其他页面处理过程。
当文件使用动态脚本节点下载时,返回的代码通常立即执行(除了Firefox和Opera,它们将等待此前的所有动态脚本节点执行完毕)。
大多数情况下,我们希望调用一个函数就可以实现Javascript文件的动态下载。下面的函数封装实现了标准实现和IE实现:
function loadScript(url, callback){ var script = document.createElement ("script") ; script.type = "text/javascript"; if (script.readyState){ //IE script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; callback(); } }; } else { //Others script.onload = function(){ callback(); }; } script.src = url; document.getElementsByTagName("head")[0].appendChild(script); } loadScript("file1.js", function(){ //调用 alert("File is loaded!"); });
此函数接受两个参数:Javascript文件的Url和一个当Javascript接收完成时触发的回调函数。属性检查用于决定监视哪种事件。最后一步src属性,并将javascript文件添加到head。
动态脚本加载是非阻塞Javascript下载中最常用的模式,因为它可以跨浏览器,而且简单易用。
Ⅱ 如何用node.js模拟多线程
Fibers 又称纤程,可以理解为协同程序,类似py和都有这样的模型。使用Fibers可以避免对资源的互抢,减少cpu和内存的消耗,但是Fibers并不能够真正的并行执行,同一时刻只有一个Fibers在执行,如果在其中一个Fibers中执行过多的cpu操作或者写了个死循环,则整个主程序将卡死住。node中的异步事件循环模型就有点象这个。
Threads 又称线程,他可以在同一时刻并行的执行,他们共享主进程的内存,在其中某一时刻某一个threads锁死了,是不会影响主线程以及其他线程的执行。但是为了实现这个模型,我们不得不消耗更多的内存和cpu为线程切换的开销,同时也存在可能多个线程对同一内存单元进行读写而造成程序崩溃的问题。
很多让node支持多线程的方法是使用c/c++的addon来实现,在需要进行cpu密集型计算的地方,把js代码改写成c/c++代码,但是如果开发人员对c++不是很熟悉,一来开发效率会降低不少,二来也容易出bug,而且我们知道在addon中的c++代码除了编译出错外,是很难调试的,毕竟没有vs调试c++代码方便。
令人振奋的消息,我们为什么不让node也支持多线程模型呢?于是Jorge为我们开发出了一个让node支持多线程模型的模块:threads_a_gogo
github地址:https://github.com/xk/node-threads-a-gogo
有了threads-a-gogo(以下简称TAGG)这个模块之后,我们可以让node做更多的事情,我记得以前我看过一篇文章,说node只能应付i/o密集型场景,在cpu密集型场景将完败给apache,因为apache是为每一个请求起一条线程的,所以在处理cpu密集型任务时一个线程的高强度计算不会很大程度的影响其他线程,类似的还有php的fastcgi,这也是很多拿node和php进行比较时,php的拥护者们一直提出的理论。
我们先来做一个简单的测试,用我们suqian大大最喜欢的斐波那契数组来看一下,加入了多线程的node有多么的强悍:(测试机器为4CPU)
没有使用TAGG的正常情况,异步也帮不了我们应对cpu密集型任务
function fibo (n) {
return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
}
var n=8
function back(){
if(!--n) return console.timeEnd('no thread');
}
console.time('no thread');
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
我们模拟了8个异步的行为,测试用的node v0.8.16版本,所以 process.nextTick还是异步方法。最后我们输出结果为:
165580141
165580141
165580141
165580141
165580141
165580141
165580141
165580141
no thread: 23346ms
接下来我们使用TAGG模块来测试同样的执行8次斐波那契数组计算,看看成绩如何?
function fibo (n) {
return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
}
console.time('8 thread');
var numThreads= 8; //创建线程池,最大数为8
var threadPool= require('threads_a_gogo').createPool(numThreads).all.eval(fibo); //为线程池注册程序
var i=8;
var cb = function(err,data){ //注册线程执行完毕的回调函数
console.log(data);
if(!--i){
threadPool.destroy();
console.timeEnd('8 thread');
}
}
threadPool.any.eval('fibo(40)', cb); //开始向线程池中执行fibo(40)这个任务
threadPool.any.eval('fibo(40)', cb);
threadPool.any.eval('fibo(40)', cb);
threadPool.any.eval('fibo(40)', cb);
threadPool.any.eval('fibo(40)', cb);
threadPool.any.eval('fibo(40)', cb);
threadPool.any.eval('fibo(40)', cb);
threadPool.any.eval('fibo(40)', cb);
最重的结果:
165580141
165580141
165580141
165580141
165580141
165580141
165580141
165580141
8 thread: 9510ms
相比不使用多线程模型的node,使用了TAGG模块之后,我们在4CPU服务器上的测试结果要快上一倍还不止。
到这里我们看上去找到了一个比较完美的解决方案应对CPU密集型任务,但是可能有同学会说,我可以使用cluster来做相同的事情,下面我们来做一个使用cluster计算这些任务的情况:
var cluster = require('cluster');
var numCPUs = 8;
function fibo (n) {
return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
}
console.time('8 cluster');
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
var i = 8;
cluster.on('exit', function(worker, code, signal) {
if(!--i){
console.timeEnd('8 cluster');
process.exit(0);
}
});
} else {
console.log(fibo (40));
process.exit(0);
}
代码上的复杂程度比使用TAGG要高的多,而且如果是动态计算斐波那契数组的结果,编码将更加困难,需要在fork时挂上不同的参数,出错的几率也更大。同时还有更重要的一个事情,如果是创建一个http服务器,如果4个cluster都在计算fibo,那第5个请求node将无法处理,而是用TAGG则还是能够正常处理的,所以cluster并不能解决单线程模型的cpu密集计算带来的阻塞问题,我们看下测试结果:
165580141
165580141
165580141
165580141
165580141
165580141
165580141
165580141
8 cluster: 11925ms
TAGG模块还有其他更多的功能,比如事件触发,平滑退出,查看线程工作状态等等,总之TAGG模块给node注入了新的活力,让node一直饱受诟病的处理cpu密集任务问题得到了一个妥善的解决,就算你不擅长c++代码,也能够轻松编写出多线程的真正的非阻塞node程序了。
Ⅲ javascript中怎么让主线程暂停一会儿再执行,就像Java中的Thread.sleep()一样
你可以用setTimeout函数和setInterval函数来达到间隔一段时间后执行某段代码的需求,但是这两个函数都是不阻塞线程的。