js處理非同步的幾種方式
同步、非同步
1.造成原因:js是單執行緒的語言
2.概念:
- 同步任務:實時處理 在主執行緒上排隊執行的任務,按照先後順序執行
- 非同步任務:分時處理 不進入主執行緒、而進入"任務佇列"的任務,只有等主執行緒任務執行完畢,"任務佇列"開始通知主執行緒,請求執行任務,該任務才會進入主執行緒執行
- 非同步模式可以一起執行多個任務
3.js處理非同步的幾種方式
1. 底層原理
XML
的英文全稱是 EXtensible Markup Language
,即可擴充套件標記語言。
1.1 使用xhr
發起GET請求
// 1. 建立 XHR 物件 var xhr = new XMLHttpRequest() // 2. 呼叫 open 函式 xhr.open('GET', 'http://www.shanshan.top:3006/api/getlist?id=1&list=統計') //?id=1查詢字串 // 3. 呼叫 send 函式 xhr.send() // 4. 監聽 onreadystatechange 事件 xhr.onreadystatechange = function () { 當 readyState 等於 4 且狀態為 200 時,表示響應已就緒: if (xhr.readyState === 4 && xhr.status === 200) { // 獲取伺服器響應的資料 console.log(xhr.responseText) } }
1.2 使用xhr
發起POST
請求
// 1. 建立 xhr 物件 var xhr = new XMLHttpRequest() // 2. 呼叫 open 函式 xhr.open('POST', 'http://www.shanshan.top:3006/api/addlist') // 3. 設定 Content-Type 屬性(固定寫法) xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') // 4. 呼叫 send 函式 xhr.send('bookname=三毛&author=張三&publisher=本地圖書出版社') // 5. 監聽事件 //onreadystatechange 儲存函式,每當 readyState 屬性改變時,就會呼叫該函式 xhr.onreadystatechange = function () { // 物件的 readyState 屬性,用來表示當前 Ajax 請求所處的狀態 // 當 readyState 等於 4 且狀態為 200 時,表示響應已就緒: if (xhr.readyState === 4 && xhr.status === 200) { // 獲取伺服器響應的資料 console.log(xhr.responseText) } }
1.3 FormData
物件管理表單資料
Ajax 操作往往用來提交表單資料。為了方便表單處理,HTML5
新增了一個 FormData
物件,可以模擬表單操作:
// 1. 新建 FormData 物件 var fd = new FormData() // 2. 為 FormData 新增表單項 fd.append('uname', 'zs') fd.append('upwd', '123456') // 3. 建立 XHR 物件 var xhr = new XMLHttpRequest() // 4. 指定請求型別與URL地址 xhr.open('POST', 'http://www.liulongbin.top:3006/api/formdata') // 5. 直接提交 FormData 物件,這與提交網頁表單的效果,完全一樣 xhr.send(fd)
FormData
物件也可以用來獲取網頁表單的值,示例程式碼如下:
// 獲取表單元素
var form = document.querySelector('#form1')
// 監聽表單元素的 submit 事件
form.addEventListener('submit', function(e) {
e.preventDefault()
// 根據 form 表單建立 FormData 物件,會自動將表單資料填充到 FormData 物件中
var fd = new FormData(form)
var xhr = new XMLHttpRequest()
xhr.open('POST', 'http://www.liulongbin.top:3006/api/formdata')
xhr.send(fd)
xhr.onreadystatechange = function() {}
})
2. 原生ajax
2.1 jQuery中的Ajax
$.ajax({
type:'GET', //請求方式
url:'http://www.shanshan.top:3006/api/getlist', //請求的url地址
data:{id:1}, //這次請求需攜帶的引數
success:function(res){ //請求成功之後的回撥函式
console.log(res) //res 後臺返回的資料
}
})
$.ajax({
type:'POST',
url:'http://www.shanshan.top:3006/api/addlist',
data:{
name:'張三',
sex:'男'
},
success:function(res){
console.log(res)
}
})
2.2 通過Ajax提交表單資料
<from id="from1">
<input type="text" name="username">
<input type="password" name="password">
<button type="submit">提交</button>
</from>
<script>
$('#from1').submit(function (e) {
e.preventDefault() //阻止表單的預設提交和頁面的跳轉
var data = $(this).serialize() //一次性獲取表單的資料,必須為每個表單元素新增 name 屬性
})
</script>
多次呼叫ajax時,不是按照書寫ajax程式碼的順序返回結果,如果存在依賴關係就需要巢狀,會造成回撥地獄
$.ajax({
url: 'http://localhost:3000/data',
success: function(data) {
console.log(data)
$.ajax({
url: 'http://localhost:3000/data1',
success: function(data) {
console.log(data)
$.ajax({
url: 'http://localhost:3000/data2',
success: function(data) {
console.log(data)
}
});
}
});
}
});
3. promise物件主要解決非同步深層巢狀造成回撥地獄的問題--es6
promise物件是一個建構函式,用來生成Promise例項
3.1 基本用法
function timeout() {
var p = new Promise(function(resolve,reject){
//非同步操作
setTimeout(function(){
var flag = false;
if(flag) {
//3.失敗時呼叫resolve()
resolve('hello');
}else{
//4.成功是呼叫reject()
reject('出錯了');
}
}, 100);
});
return p
}
//p.then獲取處理結果
p.then(function(ret){
從resolve得到的正常結果
},function(ret){
從reject得到的錯誤資訊
})
3.2 基於Promise傳送Ajax請求
<script type="text/javascript">
/*
基於Promise傳送Ajax請求
*/
function queryData(url) {
# 1.1 建立一個Promise例項
var p = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState != 4) return;
if(xhr.readyState == 4 && xhr.status == 200) {
# 1.2 處理正常的情況
resolve(xhr.responseText);
}else{
# 1.3 處理異常情況
reject('伺服器錯誤');
}
};
xhr.open('get', url);
xhr.send(null);
});
return p;
}
#傳送多個ajax請求並且保證順序
多個ajax任務通過 .then 的方式變成了線性關係,保證的執行的順序
queryData('http://localhost:3000/data')
.then(function(data){
console.log(data)
//return的是一個新的Promise物件,下一個then的呼叫者就是上一個return出來的Promise物件
return queryData('http://localhost:3000/data1');
})
.then(function(data){ //data接收上一個非同步任務返回的結果
console.log(data);
return queryData('http://localhost:3000/data2');
})
.then(function(data){ //p.then()得到非同步任務的正確結果 resolve中的資訊
console.log(data)
})
.catch(function(data){ //p.catch()獲取異常資訊 reject中的資訊
console.log(data)
})
.finally(function(){ //p.finally()成功與否都會執行(不是正式標準
console.log("成功與否都會執行")
})
</script>
- 在then方法中函式的返回值,你也可以直接return資料而不是Promise物件,在後面的then中就可以接收到資料了
3.3 Promise常用的API
1.例項方法
- p.then ( ) 得到非同步任務的正確結果 resolve中的資訊
- p.catch ( ) 獲取異常資訊 reject中的資訊
- p.finally ( ) 成功與否都會執行(不是正式標準)
2.物件方法
.all()
-
Promise.all
方法接受一個數組作引數,陣列中的物件(p1、p2、p3)均為promise例項(如果不是一個promise,該項會被用Promise.resolve
轉換為一個promise)。它的狀態由這三個promise例項決定 -
併發處理多個非同步任務,所有任務都執行完成才能得到結果
.race()
-
Promise.race
方法同樣接受一個數組作引數。當p1, p2, p3中有一個例項的狀態發生改變(變為fulfilled
或rejected
),p的狀態就跟著改變。並把第一個改變狀態的promise的返回值,傳給p的回撥函式 -
併發處理多個非同步任務,只要有一個任務完成就能得到結果
function queryData(url) {
# 1.1 建立一個Promise例項
var p = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState != 4) return;
if(xhr.readyState == 4 && xhr.status == 200) {
# 1.2 處理正常的情況
resolve(xhr.responseText);
}else{
# 1.3 處理異常情況
reject('伺服器錯誤');
}
};
xhr.open('get', url);
xhr.send(null);
});
return p;
}
/*
Promise常用API-物件方法
*/
var p1 = queryData('http://localhost:3000/a1');
var p2 = queryData('http://localhost:3000/a2');
var p3 = queryData('http://localhost:3000/a3');
Promise.all([p1,p2,p3]).then(function(result){
//all 中的引數 [p1,p2,p3] 和 返回的結果一 一對‘應["TOM", "JERRY", "SPIKE"]
console.log(result) //["TOM", "JERRY", "SPIKE"]
})
Promise.race([p1,p2,p3]).then(function(result){
// 由於p1執行較快,Promise的then()將獲得結果'P1'。 p2,p3仍在繼續執行,但執行結果將被丟棄。
console.log(result) // "TOM"
})
4.介面呼叫---fetch --- xhr的升級版
fetch不是ajax的進一步封裝,而是原生js,沒有使用XMLHttpRequest物件。
4.1 基本用法
<script type="text/javascript">
/*
Fetch API 基本用法 fetch(url).then()
第一個引數請求的路徑 Fetch會返回Promise 所以我們可以使用then 拿到請求成功的結果
*/
fetch('http://localhost:3000/fdata').then(function(data){
// text()方法屬於fetchAPI的一部分,它返回一個Promise例項物件,用於獲取後臺返回的資料
return data.text();
}).then(function(data){
// 在這個then裡面我們能拿到最終的資料
console.log(data);
})
</script>
4.2 fetch API 中的 HTTP 請求
1 GET引數傳遞 - 傳統URL 通過url ? 的形式傳參
fetch('http://localhost:3000/books?id=123', {
# get 請求可以省略不寫 預設的是GET
method: 'get'
})
.then(function(data) {
# 它返回一個Promise例項物件,用於獲取後臺返回的資料
return data.text();
}).then(function(data) {
# 在這個then裡面我們能拿到最終的資料
console.log(data)
});
2 GET引數傳遞 restful形式的URL 通過/ 的形式傳遞引數 即 id = 456 和id後臺的配置有關
fetch('http://localhost:3000/books/456', {
# get 請求可以省略不寫 預設的是GET
method: 'get'
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
3 DELETE請求方式引數傳遞 刪除id 是 id=789
fetch('http://localhost:3000/books/456', {
# get 請求可以省略不寫 預設的是GET
method: 'get'
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
4 POST請求傳參
fetch('http://localhost:3000/books', {
method: 'post',
# 3.1 傳遞資料
body: 'uname=lisi&pwd=123',
# 3.2 設定請求頭
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
fetch('http://localhost:3000/books', {
method: 'post',
body: JSON.stringify({
uname: '張三',
pwd: '456'
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
5 PUT請求傳參 修改id 是 123 的
fetch('http://localhost:3000/books/123', {
method: 'put',
body: JSON.stringify({
uname: '張三',
pwd: '789'
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
fetchAPI 中 響應格式
fetch('http://localhost:3000/json').then(function(data){
// return data.json(); // 將獲取到的資料使用 json 轉換物件
return data.text(); // // 將獲取到的資料 轉換成字串
}).then(function(data){
// console.log(data.uname)
// console.log(typeof data) string
var obj = JSON.parse(data); //轉化成物件
console.log(obj.uname,obj.age,obj.gender)
})
5.介面呼叫--axios
Axios 是一個基於 promise 的 HTTP 庫,是專注於網路資料請求的庫,可以用在瀏覽器和 node.js 中。
5.1 綜合寫法
axios({
method:"請求的方式"
url:"請求的地址"
data:傳送的資料
headers:請求頭
}).then(()=>{})
.catch(()=>{})
5.2 axios 全域性配置
# 配置公共的請求頭
axios.defaults.baseURL = 'https://api.example.com';
# 配置 超時時間
axios.defaults.timeout = 2500;
# 配置公共的請求頭
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
# 配置公共的 post 的 Content-Type
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
5.3 axios 攔截器
5.3.1 請求攔截器
-
axios.interceptors.request.use(function(config) { console.log(config.url) # 1.1 任何請求都會經過這一步 在傳送請求之前做些什麼 config.headers.mytoken = 'nihao'; # 1.2 這裡一定要return 否則配置不成功 return config; }, function(err){ #1.3 對請求錯誤做點什麼 console.log(err) })
5.3.2 響應攔截器
-
axios.interceptors.response.use(function(res) { #2.1 在接收響應做些什麼 var data = res.data; return data; }, function(err){ #2.2 對響應錯誤做點什麼 console.log(err) })
5.4 建立axios例項
-
const instance = axios.create({ baseURL: 'https://some-domain.com/api/', timeout: 1000, headers: {'X-Custom-Header': 'foobar'} });
6. **async await **讓非同步程式碼看起來更像同步程式碼 --ES7
async關鍵字 放到函式前面 (返回值是一個promise
例項物件)
await關鍵字 只能在使用async
定義的函式中使用(得到非同步的結果,await只能得到成功的結果,如果想要得到失敗的結果,只能用trycatch)
6.1處理單個非同步請求
async getFaceResult () {
//用的是try/catch來捕獲異常,把await放到try中進行執行
//如有異常,就使用catch進行處理,不會影響後面的程式碼
try {
let location = await getLocation()
} catch(err) {
console.log("錯誤",err)
}
}
6.2處理多個非同步請求
async getFaceResult () {
try {
// 新增await之後 當前的await 返回結果之後才會執行後面的程式碼
let location = await getLocation()
let ret = await getList()
} catch(err) {
console.log("錯誤",err)
}
}