Javascript實現順序語法(完整版)

首先我們來看這樣一個需求:

有這樣一組操作:pre,a1,a2,a3,b1,b2,b3,suf,每個操作都花費不確定的時長,這可能需要訪問網絡或者等待事件響應,總之我需要傳入一個回調函數然後隨它開心什麼時候去調用。

假定我們的需求是:必須在pre回調完成後才能執行A和B,並且A和B各自需要保證執行順序,而A和B之間則無需考慮順序。在A和B都執行完之後,必須執行suf。用ES5的回調函數去實現它無疑是一件令人崩潰的事情。

現在來看看我是怎麼做的:

new syn(function*(method){
	console.log('syn in');
	let pre = yield method.exc(callback, "pre", this);
	console.log(pre);

	method.syn(function*(m){
		yield m.exc(callback, "a1", this);
		yield m.exc(callback, "a2", this);
		yield m.exc(callback, "a3", this);
		return "你開心就好";
	});
	method.syn(function*(m){
		let [w1] = yield m.exc(callback, "b1", this);
		let w2 = yield m.exc(callback, "b2", this);
		let w3 = (yield m.exc(callback, "b3", this))[0];
		return w1+w2[0]+w3;
	});
	method.syn(function*(m){
		m.exc(callback, "c1", this);
		m.exc(callback, "c2", this);
		m.exc(callback, "c3", this);
		return yield m.muti();
	});
	let [A, B, C] = yield method.muti();
	console.log(A);
	console.log(B);
	console.log(C);

	method.exc(callback, "suf1", this);
	method.exc(callback, "suf2", this);
	let [suf1, suf2] = yield method.muti();
	console.log(suf1);
	console.log(suf2);
	console.log("syn out");
});

function callback(name, func) {
	console.log("執行函數:"+name);
	setTimeout(func, Math.ceil(Math.random()*2)*1000, "函數回調:"+name);
}

在上面的例子中我使用了A,B,C三組並行處理的數據,以便更清晰的表現函數執行過程:

在上述例子中,pre執行完畢時,對a1,b1,c1連續調用,其中A、B要求上一個調用有結果才發起下一個調用,C爲同時調用,等待全部結果一起返回。

在syn塊中,僅當上一個yield表達式取得結果後,下面的代碼才能夠運行

實際上syn的實現原理十分簡單,只是利用了ES6的語法糖,其本質上依然是回調函數的嵌套,並沒有開啓新的線程。但採用syn塊後,代碼的可讀性慘遭巨大提升。

以下是syn方法的具體代碼:

function syn(func, onreturn=null) {
	let method = {};
	let it = func.call(func, method);

	let ly;
	let results;
	let count = 0;
	let callback = (index,result) =>{
		count--;
		if(ly.value) {
			results[index] = result;
			if(!count) {
				ly = it.next(results);
			}
		} else {
			ly = it.next(result);
		}
		if(onreturn&&ly.done) {
			//console.log(ly.done);
			onreturn(ly.value);
		}
	};
	
	// 返回之前請求的全部值
	method.muti = ()=>{results = new Array(count);return true;};

	// 特別的,如果對多路併發請求需要進行並行處理的,可以調用syn方法‘開啓子線程’
	// syn方法應當配合muti使用
	method.syn = (fun)=>{
		let index = count++;
		new syn(fun, (v)=>{
			callback(index, v);
		});
		return false;
	};
	// 執行外部方法,此處必須使用function,不能使用箭頭函數,否則arguments無法獲取外部傳入的參數
	method.exc = function(){
		let index = count++;
		let target = arguments[0];
		let args = [];
		for(let i=1; i<arguments.length; i++) {
			if(func==arguments[i]) {
				args.push(function(){
					callback(index, arguments);
				});
			} else {
				args.push(arguments[i]);
			}
		}
		target.apply(func, args);
		return false;
	};
	// 擴展
	method.next = (v)=>it.next(v);

	ly = it.next();
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章