JavaScript同步和异步问题

一、什么叫同步、异步

  1. 同步:同步操作没有结束之前,后面的代码是无法执行的。
  2. 异步:异步操作没有结束之前,后面的代码是可以执行的。

同步:在主线程排队的任务,只有前一个任务执行完毕才能执行后一个任务。
异步:不进入主线程,而进入“任务队列”,只有等待主线程任务执行完毕,“任务队列”开始通知主线程,请求执行任务,该任务才会进入主线程执行

二、哪些是异步问题

  1. 计时函数:setTimeout()、setInterval()。
  2. 资源加载(I/O操作):在JavaScript脚本代码中动态添加资源。
  3. XHR请求:利用ajax技术向服务器发出请求

三、异步问题产生的实际案例

1、计时函数产生的异步问题:

function f1(){
	console.log('第一个');
}
function f2(){
	setTimeout(function(){
		console.log('第二个');
	},0)	//事件为0,整个操作也是异步的
}
function f3(){
	console.log('第三个');
}
f1();
f2();
f3();
(1)ES4/ES5解决方案:使用回调函数解决

将异步操作的内容进行改造,设置一个回调函数作为参数保证同步性。

function f1(){
	console.log('第一个');
}
function f2(callback){	//函数作为形参
	window.setTimeout(function(){
		console.log('第二个');
		callback();
	},2000);
}

function f3(){
	console.log('第三个');
}
f1();
f2(f3);	//将函数名f3传递给形参callback,callback成为了函数f3的一个引用
(2)回调函数作为参数
  • 可以是一个已经定义好的函数名
  • 可以是一个当时定义的匿名函数
f1();
f2(function(){
	//可以书写想与f2函数的执行内容同步的任意代码
	var m=100,n=1000;
	console.log(m+n);
});

实际应用:jQuery中有一个方法animate({},duration,callback)

$('#ball').animate({
		"margin-left":"+=200px"
	},2000,function(){
		//当动画结束之后执行该函数内部的代码
	})
(3)当有两个异步操作是如何通过回调函数处理
function f1(){
	console.log('第一个');
}
function f2(callback,callback2){
	window.setTimeout(function(){
		console.log('第二个');
		callback(callback2);
	},1000)
}
function f3(callback){
	window.setTimeout(function(){
		console.log('第三个');
		callback();
	},2000)
}
function f4(){
		console.log('第四个');
}
f1();
f2(f3,f4);

结论:回调函数容易造成“回调地狱”。

2、资源加载的异步问题:

// var imgNode = document.createElement('img');
var img = new Image();
img.src = "img/a.jpg";
pic.appendChild(img);
console.log(img.width);	    //0
console.log(img.height);	//0

解决方案:可以将输出涉及到资源的代码写在资源的onload事件中,若资源加载失败,可以将加载失败后执行的代码写在资源的onerror事件中。
例:
利用回调函数将加载图片的代码进行封装。

function loadImage(parent,url,success,failuer){
	var img = new Image();
	img.src = url;
	parent.appendChild(img);
	img.onload = function(){
		success();
	}
	img.onerror = function(){
		failuer();
	}
}

loadImage(pic,"img/qa.jpg",function(){
	console.log('图片加载成功了');
},function(){
	console.log('图片加载失败了');
})

结论:回调函数可以解决异步问题。

四、ES6引入了Promise对象解决异步问题

1、语法格式:

var promise = new Promise(function(resolve,reject){
	//书写异步操作的代码
	if(异步操作执行成功){
		resolve();
	}
	if(异步操作执行失败){
		reject();
	}
})

promise.then(function(){
	//书写调用了resolve()时执行的代码
},function(){
	//书写调用了reject()时执行的代码
})

2、例1:

加载一张图片,在图片加载成功后输出图片的宽度和高度,若图片加载失败则显示加载失败的提示文本。

let promise = new Promise(function(resolve,reject){
	var img = new Image();
	img.src = "img/a.jpg";
	pic.appendChild(img);
	img.onload = function(){
		resolve(this);
	}
	img.onerroe = function(){
		reject();
	}
})

promise.then(function(obj){
	console.log(obj)
	console.log(`图片宽度=${obj.width}`);
	console.log(`图片高度=${obj.height}`);
},function(){
	console.log(`图片加载失败`);
})

3、例2:利用Promise封装加载一张图片的功能。

function loadImage(parent,url){
	return new Promise((resolve,reject)=>{
		var img = new Image();
		img.src = url;
		parent.appendChild(img);
		img.onload = function(){
			resolve(this);
		}
		img.onerror = function(){
			reject();
		}
	})
}

loadImage(pic,'img/a.jpg').then(function(obj){
	console.log('图片加载成功');
	console.log(obj.width);
}).catch(function(){
	console.log('图片加载失败');
})

4、例3:封装confirm()

function $confirm(message){
	return new Promise((resolve,reject)=>{
		let temp = window.confirm(message);
		if(temp){
			resolve();
		}else{
			reject();
		}
	})
}

$confirm('您确定关闭吗?').then(()=>{
	console.log('确认关闭');
}).catch(()=>{
	console.log('取消关闭');
})

5、Promise技术解决异步问题的优势:

  1. 可以有多个then()连缀书写。
  2. Promise技术避免了回调地狱。
  3. 不仅给出了异步操作成功的代码解决方案,还给出了异步操作失败和完成的解决方案。
    new Promise(function(resolve,reject){
    	//异步操作
    }).then(function(){
    	//异步操作成功
    }).catch(function(){
    	//异步操作失败
    }).finally(function(){
    	//异步操作完成
    })
    

6、例四:加载多个图片

当所有图片都加载成功后,输出“所有图片都加载成功”;有任意图片没有加载成功,输出“有图片没有加载成功”。

静态方法Promise.all(array)
功能:返回一个Promise实例,参数array是一个元素为Promise实例的数组。当数组中所有的Promise实例均执行resolve()方法后,整个Promise实例才会表示成功。

静态方法Promise.race(array)
功能:与Promise.all()相反。

function loadImage(parent,url){
	return new Promise((resolve,reject)=>{
		let img = new Image;
		img.src = url;
		parent.appendChild(img);
		img.onload = function(){
			resolve();
		}
		img.onerror = function(){
			reject();
		}
	})
}

let imgs = ['01.jpg','02.jpg','03.jpg','04.jpg','05.jpg'];
let pro = [];
for(var i=0;i<imgs.length;i++){
	let temp = loadImage(box,'img/'+imgs[i]);
	pro.push(temp);
}
console.log(pro);
//静态方法Promise.all()需要一个由promise组成的数组
Promise.all(pro).then(()=>{
	console.log('所有图片都加载成功了');
}).catch(()=>{
	console.log('有图片没有加载成功');
})

7、封装ajax技术:

  1. 原生JavaScript:XMLHTTPRequest()
  2. jQuery:$.ajax()
  3. Vue.js:axios
  4. 小程序:wx.request()

如何利用Axios技术提交一个ajax请求:

  • $axios.get(url,{}).then().catch();
  • $axios.post(url,{}).then().catch()

问题:因为$axios后面还要跟具体的不同两个方法(get()post()),所以不能使用function进行封装。
解决:使用类封装(混合模式:属性定义在构造函数中,方法定义在原型下)。、
基本格式:

// 先创建构造函数
function Axios(){}

// 创建Axios类的方法
Axios.prototype.get=function(){
}
Axios.prototype.post=function(){
}

// 创建Axios类的实例
let $axios = new Axios();

$axios.get();      //发送get请求
$axios.post();     //发送post请求

封装ajax技术

//先创建构造函数
function Axios(){
	this.xhr = new XMLHttpRequest();
}

//创建Axios类的方法
Axios.prototype.toString = function(data){
	let temp = [];
	for(let i in data){
		temp.push(`${i}=${data[i]}`);
	}
	return temp.join('&');
}
Axios.prototype.get = function(url,data){
	return new Promise((resolve,reject)=>{
		data=data.constructor===Object?this.toString(data):data;
		this.xhr.open('get',url + '?' + data);
		this.xhr.send(null);
		this.xhr.onreadystatechange = function(){
			if(this.readyState === 4){
				if(this.status === 200){
					resolve(this.responseText.trim());
				}
			}
		}
	})
	
}
Axios.prototype.post = function(url,data){
	return new Promise((resolve,reject)=>{
		data=data.constructor===Object?this.toString(data):data;
		this.xhr.open('post',url);
		this.xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
		this.xhr.send(data);
		this.xhr.onreadystatechange = function(){
			if(this.readyState === 4){
				if(this.status === 200){
					resolve(this.responseText.trim());
				}
			}
		}
	})
}

//创建Axios类的实例
let $axios = new Axios();


//调用Axios类的方法
btn.onclick = function(event){
	event.preventDefault();
	let url = 'checkGradeG.jsp';
	let data = {
		exno:'100100',
		exname:'张三'
	};
	$axios.post(url,data).then(res=>{
		console.log(JSON.parse(res));
	})
}


//向服务器提交请求时的参数格式(jQuery的$ajax()):
//1、字符串:a=10&b=20【原生JavaScript只支持字符串】
//2、对象:{a:10,b:20}
//3、数组:[{name:'a',value:10},{name:'b',value:20}]

也可以将封装的内容和对封装的使用进行分离
例如单独写一个js文件axios.js,用的时候直接引入就行了。

(function(){
//函数自执行
	//先创建构造函数
	function Axios(){
		this.xhr = new XMLHttpRequest();
	}

	//创建Axios类的方法
	Axios.prototype.toString = function(data){
		let temp = [];
		for(let i in data){
			temp.push(`${i}=${data[i]}`);
		}
		return temp.join('&');
	}
	Axios.prototype.get = function(url,data){
		return new Promise((resolve,reject)=>{
			data=data.constructor===Object?this.toString(data):data;
			this.xhr.open('get',url + '?' + data);
			this.xhr.send(null);
			this.xhr.onreadystatechange = function(){
				if(this.readyState === 4){
					if(this.status === 200){
						resolve(this.responseText.trim());
					}
				}
			}
		})
		
	}
	Axios.prototype.post = function(url,data){
		return new Promise((resolve,reject)=>{
			data=data.constructor===Object?this.toString(data):data;
			this.xhr.open('post',url);
			this.xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
			this.xhr.send(data);
			this.xhr.onreadystatechange = function(){
				if(this.readyState === 4){
					if(this.status === 200){
						resolve(this.responseText.trim());
					}
				}
			}
		})
	}

	//创建Axios类的实例
	//window暴露变量
	window.$axios = new Axios();
})();


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值