优雅异步编程方式

1,073 阅读3分钟

并发

同一时间处理多件事情的能力,滚动屏幕,动态请求内容,然后进行渲染

var count = 0,ary = [],total = 30;
for(var i = 0;i < total;i++)
{
	(function(index)
	{
		ajax.(url,function(result)
		{
			ary[index] = result;
			count++;
			if(count == total)
			{
				paint();
			}
		})
	})(i)
}

对于并发处理的多个任务,如果任务与任务之间没有联系,那么这些任务是可以并行执行的,如果任务与任务之间有依赖,那么这些任务就需要串行执行了,因此对于并发的任务处理可以总结为两种情况,一种是并行的任务处理,另一种是串行的任务处理。

并发模型

  • 多线程与锁 ——JAVA
  • CSP(顺序进程通信)——Erlang、Golang
  • 事件循环——Javascript

为什么Javascript不选择多线程?

  1. 简单,不涉及共享数据与锁的处理(最大的共享数据就是DOM,试想下如果多个线程共同操作DOM的可怕后果)
  2. 浏览器的环境并没有很强的并行需求

事件循环

while(1)
{
	var event = EventQueue.shift();
	event.handler();
}

为什么要使用异步? Javascript引擎的并发模型是单线程的事件循环模型,如果对于耗时长的操作使用同步,会使单线程的事件循环模型性能大大降低。

如何获取异步的结果? 为了能获取异步操作的结果,程序使用callback的方式,当操作完成后,往事件队列push一个事件,当事件循环处理这个事件时,发起异步操作时传入的callback就会被调用。

Callback的问题

  1. 代码结构不清晰
  2. 函数复用性差

如何优雅的处理异步

  1. Thunk github.com/thunks/thun…
  2. Promise www.promisejs.org/
  3. RxJS reactivex.io/
  4. generator

Thunk

<!DOCTYPE HTML>
<html lang="zh">
<head>
</head>
<body>
<script src="./thunk.js"></script>
<script>
function get(url,callback)
{
	setTimeout(function()
	{
		callback([1,2,3])
	},500)
}


var thunk = thunks();

var t = thunk(1);
thunk(function(callback)
{
	//return 1
	callback(null,1)
})(function(e,v)
{
	console.log(e);
	console.log(v);
	return thunk(function(callback)
	{
		setTimeout(function()
		{
			callback(null,3)
		},1000)
	})
})(function(e,v)
{
console.log(e);
console.log(v)
})
/*
var getThunk = thunk.thunkify(get,'url');
getThunk(function(data)
{
	console.log(data)
})
*/
</script>
</body>
</html>

Promise

<!DOCTYPE HTML>
<html lang="zh">
<head>
</head>
<body>
<script>
function get(url)
{
	return new Promise(function(resolve,reject)
	{
		setTimeout(function()
		{
			resolve([1,2,3])
		},500)
	})
}

function handle(p)
{
	return new Promise(function(resolve,reject)
	{
		p.then(function(data)
		{
			for(var i = 0,len =data.length;i < len;i++)
			{
				data[i]++;
			}
			resolve(data);
		})
	})
}

var p = get('url');
p = handle(p);
p.then(function(data)
{
	console.log(data);
})
</script>
</body>
</html>

RxJS

<!DOCTYPE HTML>
<html lang="zh">
<head>
</head>
<body>
<button id="a" page="1"></button>
<script src="./rx.js"></script>
<script>
//Example 1
/*var comments = [
	{contentid : '1',content : 'afasfsadf',uin : '1'},
	{contentid : '1',content : 'afasfsadf',uin : '2'},
	{contentid : '1',content : 'afasfsadf',uin : '3'},
	{contentid : '1',content : 'afasfsadf',uin : '4'},
	{contentid : '1',content : 'afasfsadf',uin : '4'}
]
function getComment(page,pagesize)
{
	return new Promise(function(res,rej)
	{
		setTimeout(function()
		{
			res(comments)
		},500)
	});
}
function getNick(uin)
{
	return new Promise(function(res,rej)
	{
		res('w');
	})
}
function getLevel(uin)
{
	return new Promise(function(res,rej)
	{
		res(10);
	})
}
var clicks = Rx.Observable.fromEvent(document.getElementById('a'),'click')
.pluck('target','attributes','page','value')
.flatMap(function(data)
{
	return getComment(data,10)
})
.flatMap(function(data)
{
	var ary = [];
	for(var i = 0;i < data.length;i++)
	{
		(function(index)
		{
			ary[index] = new Promise(function(res,rej)
			{
			
				Promise.all([getNick(data[index].uin),getLevel(data[index].uin)]).then(function(nickandlevel)
				{
					data[index].nick = nickandlevel[0];
					data[index].level = nickandlevel[1];
					res(data[index]);
				})
			})
		})(i)	
	}
	return  Promise.all(ary);
})
clicks.subscribe(function(data)
{
	console.log(data)
})*/

//Example 2
/*var ob1 = Rx.Observable.create(function(observer)
{
	observer.onNext(1);
})

var ob2 = Rx.Observable.create(function(observer)
{
	observer.onNext(2);
})

Rx.Observable.merge(ob1,ob2).bufferWithCount(2).subscribe(function(data)
{
	console.log(data);
})*/
</script>
</body>
</html>

generator

<!DOCTYPE HTML>
<html lang="zh">
<head>
</head>
<body>
<script src="./co.js"></script>
<script>
//Example 1
/*
function sleep(t)
{
	return function(callback)
	{
		setTimeout(function()
		{
			callback()
		},t)
	}
}

function get(url)
{
	return function(callback)
	{
		setTimeout(function()
		{
			callback([1,2,3])
		},500)
	}
}

co(function *()
{
	yield sleep(2000);
	var data = yield get('url');
	console.log(data);
})*/

//Example 2
/*
var script = "onmessage = function(e) { postMessage('msg from worker'); }";
function CoWorker(script)
{
	return function(callback)
	{
		var blob = new Blob([script]);

		var blobURL = window.URL.createObjectURL(blob);

		var worker = new Worker(blobURL);
		worker.onmessage = function(e)
		{
			callback(e.data);
		};
		worker.postMessage(''); 
	}	
}

co(function *()
{
	var data = yield CoWorker(script)
	console.log(data)
})*/

</script>
</body>
</html>
//co.js
function co(generator) {

	var gen = generator();
	function next(result) {
		
		var step = gen.next(result);
		if (!step.done) {
			step.value(next);
		} 
	}
	next();
}