為什麼會出現非同步
js是一門為瀏覽器而誕生的語言,發展到現在,js已經不僅僅只在瀏覽器上執行了,服務端也可以執行js,像node。而js最初設計是單執行緒,也就是說會一行一行的執行,下面需要等待上面程式碼執行完畢,也就是說在特定的時刻只能做特定的事情,阻塞其他程式碼的執行。
常用的非同步例如網路請求、讀取檔案等。
非同步解決方案
瞭解javascript為什麼會出現非同步,那麼我們該怎麼去解決非同步呢?如何書寫日常工作中的工程程式碼呢?javascript的演變中出現瞭如下幾種解決方案。
callback
一般在網路請求中我們並不知道服務端啥時候返回結果,在這個等待的過程中,我們不能讓頁面一直卡頓或是不能操作的狀態,這樣會給使用者很不好的體驗感覺,我們又需要在服務端返回結果的時候來進行相應的操作或是資料更新。於是回撥函式就成為非同步的第一個解決方案,如下:
$.ajax({
"url": "",
success: function(data){
//函式返回成功需要進行的操作或資料更新
},
error: function(err){
//函式返回失敗需要進行的操作或提示
}
})
這是在jq中我們非常熟悉的一個操作,當我們去請求服務端時,不管成功或失敗,服務端都會返回一個結果,success和error函式就是一個callback函式,不管成功或是失敗,我們都能進行相應的操作或是提示,在網路請求的過程中,並不影響使用者對別的操作(除非不想讓使用者操作,必須要等待這個操作完成),當服務端返回資料時,我們可以通過callback接收到,這個時候我們再進行相應的操作。
Promise
在callback中,如果當前網路請求需要依賴前一個網路請求,我們就有可能掉進回撥地獄,
如圖:
於是promise的出現為我們解決了這個問題。promise是在ES6中被統一規範,由瀏覽器直接支援,promise為一諾千金的意思,意思是在將來不管這個狀態成功或是失敗,都將返回一個結果。
promise有三種狀態:
等待中 pending;
已完成 fulfilled;
已拒絕 reject;
let test = new promise(resolve,reject){
if(success){
resolve();
}else{
reject();
}
}
test()
.then(res => {
//成功時返回函式
})
.catch(err => {
//失敗時返回函式
})
Generator
我們希望以同步方式寫非同步程式碼,可以使邏輯清晰,程式碼簡潔,為了實現這種目的,我們又演進出generator方案,
function * add(x){
yield x + 50;
yield x + 100;
return x + 150;
}
let test = add(5);
test.next(); //done: true , value: 55
test.next(); //done: true , value: 105
test.next(); //done: true , value: 155
如上,genertor與普通函式區別不大,function後面加了一個*,
首先定義了一個test = add(5),函式沒有執行,只是生成了一個genertor物件,函式屬於暫停狀態,只有當執行.next()時,才會啟用暫停狀態,開始執行內部程式碼,遇到第一個yield才會返回執行結果,並記住上下文,暫停,交出控制權,再次執行,找到第二個yield,並重覆上述步驟
function* Fn(){
var result = yield ajax(url);
var result1 = yield ajax(url1);
}
let F = Fn();
var result = F.next();
result.value
.then(res => {
F.next();
})
.catch(err => {
})
如上 封裝一個ajax非同步,定義一個genertor物件,執行請求交出控制權,根據返回結果執行第二個ajax請求
asnyc/await
ES7的asnyc/await號稱是非同步的終極解決方案,讓我們以同步的方式來書寫非同步程式碼,這樣看起來更簡潔,邏輯更清晰。
比如微信小程式煩人的非同步:
const Login = data => {
return new Promise( (resolve, reject) => {
wx.login({
success: res => {
resolve(res);
},
fail: err => {
reject(err);
}
})
})
}
const getUserInfos = data => {
return new Promise((resolve,reject){
wx.getUserInfo({
success: res => {
resolve(res);
},
fail: err => {
reject(err);
}
})
})
}
const sendCode = data = > {
return new Promise((resolve,reject){
wx.request({
url: '',
success: res=> {
resolve(res);
},
fail: err => {
reject(err);
}
})
})
}
以上就是封裝好的微信登入過程中的獲取code userInfo 和傳送ajax請求的幾個函式,
然後
asycn function getLogin(){
try {
let result_login = await Login();
let result_userInfo = await getUserInfos();
result_userInfo['code'] = result_login['code'];
let result_sendCode = await sendCode(result_userInfo );
return result_sendCode;
}catch(err){
console.log(err);
}
}
getLogin()
.then(res => {
})
.catch(err => {
}
async與genertor的寫法類似,具備更好的語義,並且返回的是promise