The code should be written in a way that others cannot understand (22)

This article refers to the book "JavaScript Design Patterns"-Zhang Rongming

Preface

  In this section we deal with an old-fashioned problem, asynchronous. Why does the problem of asynchrony occur? This should start before JS was born.

  A long, long time ago, the sky and the earth had not been separated, and the universe was in chaos. There is a giant named Pangu who has been sleeping for 18,000 years in this chaos.
Insert picture description here

  One day, Pangu woke up suddenly. Seeing the darkness around him, he raised his axe and slammed into the darkness in front of him. After hearing a loud noise, the chaotic things gradually separated. The light and clear things slowly rise and become the sky; the heavy and turbid things slowly fall and become the earth...

  It's a long way off. The asynchronous problem is because JS was designed to be single-threaded at the beginning. When we encounter some time-consuming things, we can't just stop moving, and users may think that the computer is broken. Then you need to temporarily suspend the time-consuming thing, and wait for JS to finish the work at hand, and then deal with the time-consuming thing.

Waiter mode

  Trigger future actions by monitoring multiple asynchronous processes .

  In our work, we will often encounter data requesting different interfaces, so that the data of these two interfaces will be rendered together. Then the request is fast and sometimes slow, one is completed, and the other has not returned a result.

  The way to solve this problem and save trouble is to send another request in the callback function of one request, but the event of the entire request becomes T1 + T2 . As a programmer, it must be faster, and faster is also a block. We hope that the total time of the request event can be the larger of T1 and T2 , and the waiting mode can be used for optimization.

  We need to create a waiting object. This object does not need to monitor the completion of asynchronous logic in real time. It only needs to perform a confirmation iteration on the state of all asynchronous logic when the state of the registered asynchronous logic changes (request success or failure).

//等待对象
var Waiter = function() {
    
    
	//注册了的等待对象容器
	var dfd = [],
		//成功回调方法容器
		doneArr = [],
		//失败回调容器方法
		failArr = [],
		//缓存 Array 方法 slice
		slice = Array.prototype.slice,
		//保存当前等待对象
		that = this;
	
	//监控对象类
	var Primise = function() {
    
    
		//监控对象是否解决成功状态
		this.resolved = false;
		//监控对象是否解决失败状态
		this.rejected= false;
	}
	//监控对象类原型方法
	Primise.prototype = {
    
    
		//解决成功
		resolve: function() {
    
    },
		//解决失败
		reject: function() {
    
    }
	}

	//创建监控对象
	that.Deferred = function() {
    
    
		return new Promise();
	}
	
	//回调执行方法
	function _exec(arr) {
    
    }

	//监控异步方法 参数:监控对象
	that.when = function() {
    
    };

	//解决成功回调函数添加方法
	that.done = function() {
    
    };

	//解决失败回调函数添加方法
	that.fail = function() {
    
    };
}

  For the waiter object, it can be seen that its structure is as follows:

  • Three arrays are defined internally, namely the waiting object container, and the success and failure callback function container;
  • A class-monitoring object, the object has two attributes, namely, monitoring the successful state of the solution, monitoring the failed state of the solution, two methods, the successful solution and the failed solution;
  • A private method _exec to handle the success and failure callback function;
  • 3 public method interfaces: when method monitors asynchronous logic, done method adds success callback function, fail method adds failure callback function;

  First of all, we first implement the monitor object class prototype methods resolve and reject . They are all corresponding operations performed due to the change of the asynchronous logic state. The difference is that the resolve method is to perform a successful callback, so the state of all monitored asynchronous logic must be checked. Test. The reject method is to execute a failed callback function, so as long as a monitored asynchronous logic state becomes a failed state, the callback function must be executed immediately.

//监控对象原型方法
Primise.prototype = {
    
    
	//解决成功
	resolve: function() {
    
    
		//设置当前监控对象解决成功
		this.resolved = true;
		//如果没有监控对象则取消执行
		if(!dfd.length) return;
		//遍历所有注册了的监控对象
		for(var i = dfd.length - 1; i >= 0; i--) {
    
    
			//如果有任意一个监控对象没有被解决或者解决失败则返回
			if(dfd[i] && !dfd[i].resolved || dfd[i].rejected) return;
			//清除监控对象
			dfd.splice(i, 1);
		}
		//执行解决成功回调方法
		_exec(doneArr);
	},
	//解决失败
	reject: function() {
    
    
		//设置当前监控对象解决失败
		this.rejected = true;
		//如果没有监控对象则取消执行
		if(!dfd.length) return;
		//清除所有监控对象
		dfd.splice(0);
		//执行解决成功回调方法
		_exec(failArr);
	}
}

  The thing to do for the callback execution method is very simple, i is to traverse the success or failure callback function container, and then execute the internal methods in turn.

//回调执行方法
function _exec(arr) {
    
    
	var i = 0,
		len = arr.length;
	//遍历回调数组执行回调
	for(; i < len; i++) {
    
    
		try {
    
    
			//执行回调函数
			arr[i] && arr[i]();
		} catch(e) {
    
    }
	}
}

  The waiting object also defines three common interface methods when, done, and fail . The when method is to detect the asynchronous logic of the registered monitoring object (request: ajax request, etc.; method: setTimeout method, etc.), so the when method is To put the detection object in the detection object container, of course, it is necessary to determine whether the detection object exists, whether it is resolved, and whether it is an instance of the detection object class. Finally, the waiting object must be returned to facilitate chained calls.

//监控异步方法 参数:监控对象
that.when = function() {
    
    
	//设置监控对象
	dfd = slice.call(arguments);
	//获取监控对象数组长度
	var i = dfd.length;
	//向前遍历监控对象, 最后一个监控对象的索引值为 length - 1
	for(--i; i >= 0; i--) {
    
    
		//如果不存在监控对象,或者监控对象已经解决,或者不是监控对象
		if(!dfd[i] || dfd[i].resolved || dfd[i].rejected || !dfd[i] instanceof Primise) {
    
    
			//清理内存 清除当前监控对象
			dfd.splice(i, 1);
		}
	}
	//返回等待者对象
	return that;
};

  For done method fail function method is much simpler, the corresponding callback function only needs to add the corresponding container can callback function, and eventually returns the object's convenience waiting call chain.

//解决成功回调函数添加方法
that.done = function() {
    
    
	//向成功回调函数容器中添加回调方法
	doneArr = doneArr.concat(slice.call(arguments));
	//返回等待者对象
	return that;
};
//解决失败回调函数添加方法
that.fail = function() {
    
    
	//向失败回调函数容器中添加回调方法
	failArr = failArr.cancat(slice.call(arguments));
	//返回等待者对象
	return that;
}

  Well, with this waiting object, we can monitor the asynchronous logic. Let's first write an example of an asynchronous method.

setTimeout(function() {
    
    
	console.log('first');
}, 30)
console.log('second');
//控制台输出结果
//second
//first

  Briefly talk about the principle of synchronization. Similar to a one-way street, cars can only pass one by one, and even if there is a rocket behind, they can only line up and pass one by one.

  Then you can't always let the rocket wait. This is asynchronous, similar to opening up multiple lanes, letting those slow cars drive on one side, letting the road out, and letting the rocket pass quickly. This is asynchronous generation, so we can see that the above console outputs second first and then first .

  After explaining the asynchronous generation, we set up a scenario to verify the waiter object.

  For example, there is a page with several activity eggs, so that after all the easter eggs movement is over, the welcome page will appear. It is a headache to detect the end of several sports eggs. At this time, we need our waiter object.

var waiter = new Waiter();

  Here we simplify the easter egg, abstract it into a method, and it will end at a certain moment, we need to create a listener object inside the easter egg method execution.

//第一个彩蛋,5 秒后停止
var first = function() {
    
    
	//创建监听对象
	var dtd = waiter.Deferred();
	setTimeout(function() {
    
    
		console.log('first finish');
		//发布解决成功消息
		dtd.resolve();
	}, 5000);
	//返回监听对象
	return dtd;
}();
//第二个彩蛋,10 秒后停止
var second = function() {
    
    
	//创建监听对象
	var dtd = waiter.Deferred();
	setTimeout(function() {
    
    
		console.log('second finish');
		//发布解决成功消息
		dtd.resolve();
	}, 10000)
	//返回监听对象
	return dtd;
}();

  Finally, we need to use the waiter object to monitor the working status of the two Easter eggs, and execute the corresponding success callback function and failure callback function.

waiter
	//监听两个彩蛋
	.when(first, sceond)
	//添加成功回调函数
	.done(function() {
    
    
		console.log('success');
	}, function() {
    
    
		console.log('success again')
	})
	//添加失败回调函数
	.fail(function() {
    
    
		console.log('fail');
	});
//输出结果
// first
// second
// success
// success again

Waiter mode application

  The waiter pattern is used by many frameworks, such as the Deferred object in jQuery , and it also encapsulates the ajax method with the waiter idea .

$.ajax("text.php")
	.done(function() {
    
     console.log("成功"); })
	.fail(function() {
    
     console.log("失败"); });

  Of course, we can also encapsulate the native ajax method into a waiter mode, call the resolve method in the request success callback function, and call the reject method in the request failure callback function.

//封装 get 请求
var ajaxGet = function(url, success, fail) {
    
    
	var xhr = new XMLHttpRequest();
	//创建检测对象
	var dtd = waiter.Deferred();
	xhr.onload = function(event) {
    
    
		//请求成功
		if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
    
    
			success && success();
			dtd.resolved();
		//请求失败
		} else {
    
    
			dtd.reject();
			fail && fail();
		}
	};
	xhr.open("get", url, true);
	xhr.send(null);
};

  With the waiter object, there is no need to worry about back-end deployment in the future, we only need to modify the interface. Save a lot of work.

  In addition, the mechanism implemented by our polling (regularly sending requests to the backend) is somewhat similar to the waiter model, except that it monitors future actions within itself.

//长轮询
(function getAjaxData() {
    
    
	//保存当前函数
	var fn = arguments.callee;
	setTimeout(function() {
    
    
		$.get('./test.php', function() {
    
    
			console.log('轮询一次');
			//在执行一次轮询
			fn();
		})
	}, 5000)
})();

  The observer mode is intended to handle time-consuming operations, such as traversing and operating each pixel in a large picture in the canvas , timer operations, asynchronous requests, etc. The waiter pattern provides an abstract non-blocking solution.




Guess you like

Origin blog.csdn.net/EcbJS/article/details/111615662