非同步程式設計在JavaScript中非常重要,但是過多的非同步程式設計同時也帶來了回撥巢狀的問題。
什麼是回撥函式?
ajax(url, () => {});複製程式碼
以上程式碼就是一個回撥函式。一個函式作為引數需要依賴另一個函式執行呼叫。
但是回撥函式有一個致命弱點,容易出現回撥地獄(Callback hell)
什麼是回撥地獄?
let form = document.querySelector('form');
form.onsubmit = function (e) {
var name = document.querySelector('input').value;
$.ajax({
url: "http://demo.com/submit",
type:'POST',
data: {name: name},
success: function(res) {
if (res.code === 2000) {
var h1 = document.querySelector('h1').innerHTML;
h1.innerHTML = res.data.name;
}
}
});
}複製程式碼
像這樣,函式作為引數一層層的巢狀,使得程式碼塊看起來龐大、不清晰,不能一下子分清結構層級,這就稱為“回撥地獄”。
回撥地獄的根本問題與缺點:
- 巢狀函式存在耦合性,一旦有所改動,就會牽一髮而動全身
- 巢狀函式一多,就很難處理錯誤
- 回撥函式態使用try...catch...捕獲錯誤,不能直接return
怎麼解決回撥地獄?
主要原因是因為開發者的編碼習慣導致,我們可以通過以下方式來解決:
- 保持簡短的程式碼風格,儘量使用命名函式,避免使用匿名函式
document.querySelector('form').onsubmit = onFormSubmit();
function onFormSubmit(e) {
var name = document.querySelector('input').value;
$.ajax({
url: "http://demo.com/submit",
type:'POST',
data: {name: name},
success: onSuccess(res)
});
}
function onSuccess(res){
if (res.code === 2000) {
var h1 = document.querySelector('h1').innerHTML;
h1.innerHTML = res.data.name;
}
}複製程式碼
- 模組化,拆分每一個獨立的功能函式,封裝、打包成一個單獨的js檔案,通過import匯入
// formHandler.js
module.exports.formSubmit = onFormSubmit;
function onFormSubmit(e) {
var name = document.querySelector('input').value;
$.ajax({
url: "http://demo.com/submit",
type:'POST',
data: {name: name},
success: onSuccess(res)
});
}
function onSuccess(res){
if (res.code === 2000) {
var h1 = document.querySelector('h1').innerHTML;
h1.innerHTML = res.data.name;
}
}
// index.js
var formHandler = require('./formHandler');
document.querySelector('form').onsubmit = formHandler.formSubmit;複製程式碼
- 處理每一個錯誤,按照標準規範編碼
- Promise/Gengenerator/Async Function
除了常見的一種回撥函式作為非同步處理,還有promises,Generators,async是處理非同步處理的方式
Promise的特點是什麼?
Promise譯為承諾,承諾在以後、未來會有一個確切的回覆,並且該承諾擁有三種狀態:- 等待中(pending)
- 完成了(resolved)
- 拒絕了(rejected)
這個承諾一旦狀態變更了以後,就不能再更改狀態了
當我們在構造Promise的時候,建構函式內部的程式碼是立即執行的
new Promise((resolve, reject) => {
console.log('new Promise')
resolve('success')
})
console.log('finish');
// new Promise
// finish複製程式碼
Promise有什麼缺點?
無法取消Promise,錯誤需要通過回撥函式來捕獲
什麼是Promise鏈?Promise建構函式執行和then函式執行有什麼區別?
Promise
實現了鏈式呼叫,也就是說每次呼叫 then 之後返回的都是一個 Promise,並且是一個全新的 Promise,原因也是因為狀態不可變。如果你在 then 中使用了return,那麼 return 的值會被 Promise.reslove() 包裝Promise.resovle(1)
.then(res => {
console.log(res); // 1
return 2; // 包裝成了 Promise.reslove(2)
})
.then(res => {
console.log(res); // 2
})
複製程式碼
Promise也解決了地獄回撥的問題:
// old
ajax(url, () => {
// 處理邏輯
ajax(url1, () => {
// 處理邏輯
ajax(url2, () => {
// 處理邏輯
})
})
})
// new
ajax(url)
.then(res => {
console.log(res);
return ajax(url1);
})
.then(res => {
console.log(res);
return ajax(url2);
})
.then(res => console.log(res));
複製程式碼
async和await
await 的同步只是在 async 函式裡同步,但並不會阻塞其他外部程式碼的執行。而這也是 await 必須放在 async 函式裡的原因
async 用於申明一個 function 是非同步的,而 await 用於等待一個非同步方法執行完成。
未完待續...