一、什麼叫同步、異步:
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請求