一、什么叫同步、异步:
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(“F1”); }
function f2(callback){ //函数作为形参
window.setTimeouut(function(){
console.log(“F2”);
callback();
},2000);
}
function f3(){ console.log(“F3”); }
//执行上述函数并保证F1 F2 F3的时序性
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);
},2000)
}
function f3(callback){
window.setTimeout(function(){
console.log("第三个");
callback();
},5000)
}
function f4(){
console.log("第四个");
}
f1()
f2(f3,f4)
** 结论:回调函数容易造成“回调地狱”。**
2、资源加载的异步问题:
// var imgNode = document.createElement("img");
var img=new Image();
img.src="images/06.jpg";
pic.appendChild(img);
console.log(img.width); //0
console.log(img.height); //0
解决方案:可以将输出涉及到资源的代码书写在资源的onload事件中,若资源加载失败,
可以将加载失败后执行的代码书写在资源的onerror事件中。
例:利用回调函数将加载图片的代码进行封装。
function loadImage(parent,url,success,failure){
var img=new Image();
img.src=url;
parent.appendChild(img);
img.onload=function(){
success();
}
img.onerror=function(){
failure();
}
}
loadImage(box,"images/12.jpg",function(){
console.log("图片加载成功了");
},function(){
console.log("图片加载失败了");
})
3、结论:回调函数可以解决异步问题。
四、ES6引入了Promise对象解决异步问题:
1、语法格式:
let 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="images/2.jpg";
box.appendChild(img);
img.onload=function(){
resolve(this);
}
img.onerror=function(){
reject();
}
})
promise.then(function(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(box,'images/3.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、例4:加载多个图片。
- 当所有图片都加载成功后,输出“所有图片加载成功”;有任意图片没有加载成功,输 出“有图片没有加载成功”。
- 静态方法:Promise.all(array)
- 功能:返回一个Promise实例,参数array是一个元素为Promise实例的数组。当数组中所有的Promise实例均执行resolve()方法后,整个Promise实例才会表示成功。
- 静态方法:Promise.race(array)
- 功能:与Promise.all()相反。
let imgs=['1.jpg','2.jpg','30.jpg','4.jpg'];
let pro=[];
for(let i=0;i<imgs.length;i++){
let temp = loadImage(box,'images/' + imgs[i]);
pro.push(temp);
}
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请求