论坛首页 Web前端技术论坛

如何在JavaScript中实现某个方法执行超时后则继续执行其它方法?

浏览 18722 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-11-12  
新开窗口的呢??
0 请登录后投票
   发表时间:2007-11-12  
afcn0 写道
补充个测试
opera ie ff上面都没有显示thread2 run所以基本肯定js就是单线程的


养成写测试的习惯是很好的!事实胜于雄辩。
0 请登录后投票
   发表时间:2007-11-12  
myy 写道

据我所知,IE6 (IE7标签页方式不清楚) 用 open(), _balnk, Ctrl+N 等各种方式打开的新窗口都是在独立的线程中运行的,并且同时都属于一个IE进程,"浏览器的主线程"这种说法也不确切,事实上,同一个IE进程每个IE窗口线程都是平等的,根本没有哪个是"主线程"之说。也就是说,每个IE窗口线程中都有"全套"的东西,包括 js引擎。(不过,底层的网络通讯采用了线程池,这个是共享的)。

这样,虽然"JS本身一定是单线程的",但是open()创建了两套"在不同线程中的js引擎环境",通过这样来模拟 LZ 的要求是可行的。


首先open,ctrl-n出来的窗口,与原窗口肯定同属一个进程。你用桌面图标启动的新的IE窗口就在另外一个进程里,那不在我们的讨论范围之内。其次,你说它们都在独立的窗口线程里,maybe。但我指的是主线程,就是浏览器的主控线程,其他线程都是从这个线程上分支出来的。当然,偶不清楚IE内部构造到底是如何的。但是一个IE窗口不应该有全套的东西,否则就变成一个独立的进程了。而且即使每个窗口都一个独立的js引擎,其执行也是线性化的。这很容易推理出来,因为一个窗口的脚本可以调用另一个窗口的脚本,也就是一个窗口的脚本一定可以阻塞另一个窗口的脚本。这就是我为什么说窗口对于脚本来说没有意义的原因。

测试代码:

<html>
<head>
<title>Test JS thread</title>
<script>
function heavyWork(size) {
	info('Start a heavy work...');
	size = size || 1000;
	var result = 0;
	for (var i = 0; i < size; i++) {
	for (var j = 0; j < size; j++) {
		result += Math.random();
	}
	}
	info('Done.');
}
function test() {
	window.open('test.html', '_blank', '');
	heavyWork();
}
function info(msg) {
	var now = new Date().getTime() % 100000;
	document.getElementById('info').innerHTML += '[' + now + '] ' + msg + '<br>';
}
window.onload = function () {
	info('loaded.');
	heavyWork();
}
</script>
</head>
<body>
<div id="info">
loading...<br>
</div>
<div>
	<button onclick="test()">Open a new window with a heavy work</button>
</div>
</body>
</html>
0 请登录后投票
   发表时间:2007-11-12  
不是很理解hax写的,window.open我是这样测试的
a.htm
<div id="test">
</div>
<script>
setTimeout(function(){
eee=window.open("b.htm");
var temp=new Date().getTime();
for (var i=0; i<100000; i++ )
{
	if(window.t)
	{
	document.getElementById("test").innerHTML="thread2 run";
	}
	i=i+10000;
	i=i-10000;
}
document.getElementById("test").innerHTML+=new Date().getTime()-temp;

document.documentElement.appendChild(document.createTextNode("aaaaaaaaaa"));
//ie document.body.innerHTML+="aaaaaa"
},0)
</script>

b.htm
<div id="test"></div>
<script>
window.opener.document.documentElement.appendChild(document.createElement("hr"));
//ie window.opener.document.body.innerHTML+="<hr>"
</script>

经测试ff ie均表示open不用等待窗口load完毕继续执行下面代码,和XHR的异步差不多,并且open的窗口不是立即执行,而是等待主窗口js停止执行后才进行load动作,所以看到是先有aaaaa字符,后有hr的横线,open好似只是注册一个要打开的窗口,而不是立即执行,有点timeout意思
0 请登录后投票
   发表时间:2007-11-12  
感谢这么多人的热烈讨论,下面的代码是我继原贴后写的测试代码,大家可以在IE、FireFox、Opera等浏览器里测试一下看看结果。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>JavaScript方法执行超时测试</title>
<script>
	var executed = false; // 方法B是否执行的标志位
	var x = 0; // 方法A中用来累加计数,使方法A执行超时
	var expiredTime = 2000;

	function methodMain()
	{
		expiredTime = 1000 * document.counter.expiredTime.value;
		var t = setInterval("methodA()",0);// setInterval()
		//clearTimeout
		setInterval("methodB()",expiredTime);// setInterval() setTimeout
		//clearInterval
	}

	// 方法A
	function methodA()
	{
		// 记下methodA执行的开始时间
		document.counter.aBegin.value = getNow();
		var loopSize = document.counter.loopSize.value;
		// 方法A的耗时内容执行开始
		for(i = 0;i <= loopSize;i++)
		{
			document.counter.displayBoxA.value = "i=" + i;
		}
		// 方法A的耗时内容执行结束

		// 当方法A的耗时内容没有超过expiredTime的时间执行完成的话,就直接开始执行方法B
		methodB();
		// 设置方法B执行完成的标志位
		executed = true;
		// 记下methodA执行的结束时间
		document.counter.aEnd.value = getNow()
	}

	// 方法B
	function methodB()
	{
		// 记下methodB执行的开始时间
		document.counter.bBegin.value = getNow();
		if(executed == true)
		{
			// 执行过了......
			// document.counter.displayBox.value = '执行过了......';
		}
		else
		{
			for(var i = 0;i < 100;i++)
			{
				x = x + 1;
				document.counter.displayBoxB.value = x;
			}
		}
		// 记下methodB执行的结束时间
		document.counter.bEnd.value = getNow()
	}

	function getNow()
	{
		var now = new Date();
        var year = now.getYear();
        var month = now.getMonth();
        var day = now.getDay();
        var hour = now.getHours();
        var minute = now.getMinutes();
        var second = now.getSeconds();

		// return year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
		return new Date().format("yyyy-MM-dd hh:mm:ss");
	}

	Date.prototype.format = function(format)
	{
		var o =
		{
			"M+" : this.getMonth()+1, //month
			"d+" : this.getDate(), //day
			"h+" : this.getHours(), //hour
			"m+" : this.getMinutes(), //minute
			"s+" : this.getSeconds(), //second
			"q+" : Math.floor((this.getMonth()+3)/3), //quarter
			"S" : this.getMilliseconds() //millisecond
		}
		if(/(y+)/.test(format))
			format = format.replace(RegExp.$1,(this.getFullYear()+"").substr(4 - RegExp.$1.length));
		for(var k in o)
			if(new RegExp("("+ k +")").test(format))
				format = format.replace(RegExp.$1,RegExp.$1.length==1 ? o[k] : ("00"+ o[k]).substr((""+ o[k]).length));
		return format;
	}
	// alert(new Date().format("yyyy-MM-dd hh:mm:ss"));
</script>
</head>

<body>
<form name="counter">
	方法A执行的开始时间:<input type="text" name="aBegin" value="" size="30"/><br/>
	方法A执行的结束时间:<input type="text" name="aEnd" value="" size="30"/><br/>
	方法A计数器:<input type="text" name="displayBoxA" value="0" size="20"/><br/>

	方法A执行的超时限制时间:<input type="text" name="expiredTime" value="2" size="30"/>秒<br/>
	方法A中用来模拟执行的死循环大小:<input type="text" name="loopSize" value="5000" size="30"/>次<br/>

	方法B执行的开始时间:<input type="text" name="bBegin" value="" size="30"/><br/>
	方法B执行的结束时间:<input type="text" name="bEnd" value="" size="30"/><br/>
	方法B计数器:<input type="text" name="displayBoxB" value="0" size="20"/><br/>
	<p> </p>
	执行测试:<input type="button" name="exe" value="开 始" onclick="methodMain();"/>  
	<input type="reset" name="tReset" value="重 置"/>
</form>
</body>
</html>
0 请登录后投票
   发表时间:2007-11-13  
一切都是因为Windows程序是消息驱动的,看以下Windows程序的代码:

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
		PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT ("HelloWin") ;
	HWND	hwnd ;
	MSG	msg ;
	WNDCLAS	wndclass ;
	
	wndclass.style		  = CS_HREDRAW | CS_VREDRAW ;
	wndclass.lpfnWndProc  = WndProc ;
	wndclass.cbClsExtra	  = 0 ;
	wndclass.cbWndExtra	  = 0 ;
	wndclass.hInstance	  = hInstance ;
	wndclass.hIcon		  = LoadIcon (NULL, IDI_APPLICATION) ;
	wndclass.hCursor	  = LoadCursor (NULL, IDC_ARROW) ;
	wndclass.hbrBackground	= (HBRUSH) GetStockObject (WHITE_BRUSH) ;
	wndclass.lpszMenuNam	= NULL ;
	wndclass.lpszClassName	= szAppName ;

	if (!RegisterClass (&wndclass))
	{
		MessageBox (	NULL, TEXT ("This program requires Windows NT!"),  szAppName, MB_ICONERROR) ;
		return 0 ;
	}
	hwnd = CreateWindow( szAppName,	// window class name
			TEXT ("The Hello Program"),	// window caption
			WS_OVERLAPPEDWINDOW,	// window style
			CW_USEDEFAULT,	// initial x position
			CW_USEDEFAULT,	// initial y position
			CW_USEDEFAULT,	// initial x size
			CW_USEDEFAULT,	// initial y size
			NULL,			// parent window handle
			NULL,	        // window menu handle
			hInstance,	    // program instance handle
			NULL) ; 	    // creation parameters
	
	ShowWindow (hwnd, iCmdShow) ; // Post Message WM_SHOW
	UpdateWindow (hwnd) ;   // Post Message WM_PAINT, 这里可能不对
	
	while (GetMessage (&msg, NULL, 0, 0))
	{
		TranslateMessage (&msg) ;
		DispatchMessage (&msg) ;
	}
	return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC			hdc ;
	PAINTSTRUCT ps ;
	RECT		rect ;
	
	switch (message)
	{
		case WM_CREATE:
			return 0 ;
		
		case 	WM_PAINT:
			hdc = BeginPaint (hwnd, &ps) ;
			
			GetClientRect (hwnd, &rect) ;
			
			DrawText (hdc, TEXT ("Hello, World!"), -1, &rect,
			DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
			EndPaint (hwnd, &ps) ;
			return 0 ;
		
		case	WM_DESTROY:
			PostQuitMessage (0) ;
			return 0 ;
	}
	return DefWindowProc (hwnd, message, wParam, lParam) ;
}


一个Windows程序的入口是WinMain,这里面主要部分就是一个消息循环。IE用window.open的时候没有创建新进程,所以所有的消息都是主窗口的消息处理循环在处理。IE打开一个新窗口的时候所做的事情大概就是CreateWindow、ShowWindow、UpdateWindow,这三个方法里面并不是直接显示新窗口,而时往消息队列里面放WM_SHOW、WM_PAINT等消息,要到主消息循环处理到这些消息时才显示新窗口。

另外,setTimer,setInterval也只是让系统在若干时间后放一个WM_TIMER消息到消息队列,主消息循环处理到这个消息时才会运行setTimer和setInterval时指定的回调函数。
0 请登录后投票
   发表时间:2008-06-12  
貌似你执行某个方法 fun1();
估计其应该只执行30秒,如果超了。这个方法结束。执行另一个方法?

但是fun1还是在执行啊。
并且IE会挂掉的。。。
0 请登录后投票
   发表时间:2008-07-03  
afcn0 写道
首先js是单线程的,lz的想法就不对,那是多线程,现在基本除了XHR的callback可以另开一个线程以外,还无法实现多线程,其次timeout是在本次js代码运行完开始计时timeout的,所以在一个耗时的函数之外timeout或者interval一个函数运行本身就是荒谬的,ie下面alert,js函数执行也会阻塞interval执行,应该其他浏览器也是这么实现,我没测试,如果interval可以实现多线程,那至少ie上面也不行


js是单线程的没错,但是setTimeout确实可以解决这个问题。因为IE识别javascript是否running过长,是对每个event触发的javascript method而言。所以,分离出独立的方法,用setTimeout触发,确实可以让IE不要跳出那个可恶的error。当然,本质上没有减少运行的总体时间。
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics