JavaScript從初級往高階走系列————非同步
阿新 • • 發佈:2019-02-19
非同步
- 什麼是單執行緒,和非同步有什麼關係
- 什麼是event-loop
- 是否用過jQuery的Deferred
- Promise的基本使用和原理
- 介紹一下async/await(和Promise的區別、聯絡)
- 非同步解決方案
什麼是單執行緒,和非同步有什麼關係
單執行緒-只有一個執行緒,只做一件事。JS之所以是單執行緒,取決於它的實際使用,例如JS不可能同新增一個DOM和刪除這個DOM,所以它只能是單執行緒的。
console.log(1);
alert(1);
console.log(2);
上面這個例子中,當執行了alert(1)
,如果使用者不點選確定按鈕,console.log(2)
是不會執行的。
為了利用多核CPU的計算能力,HTML5提出WebWorker
標準,允許JavaScript指令碼建立多個執行緒,但是子執行緒完全受主執行緒控制,且不得操作DOM。所以,這個新標準並沒有改變JavaScript單執行緒的本質。
js非同步
console.log(100);
setTimeout(function(){
console.log(200);
},1000)
console.log(300);
console.log(400);
console.log(400);
.... // 這裡來很多很多個console.log(400); 結果就是列印完所有的400,等一秒再列印200
event-loop
文字解釋
- 事件輪詢,JS實現非同步的具體解決方案
- 同步程式碼,直接執行
- 非同步函式先放在非同步佇列中
- 待同步函式執行完畢,輪詢執行 非同步佇列 的函式
上面那個例子的執行效果就是這樣的:
例項分析:
這個例子中有兩種情況,取決於ajax的返回時間,如果ajax時間小於100ms它就先放進非同步佇列
Jquery Deferred
Jquery1.5前後的變化
var ajax = $.ajax({ url: 'data.json', success: function(){ console.log('success1'); console.log('success2'); console.log('success3'); }, error: function(){ console.log('error'); } }) console.log(ajax); // 返回一個xhr物件
// 鏈式操作
var ajax = $.ajax('data.json');
ajax.done(function(){
console.log('success1');
}).fail(function(){
console.log('error');
}).done(function(){
console.log()
})
console.log(ajax); // 返回一個deferred物件
- 無法改變JS非同步和單執行緒的本質
- 只能從寫法上杜絕callback這種形式
- 它是一種語法糖形式,但是解耦了程式碼
- 很好的體現:開放封閉原則(對擴充套件開放,對修改封閉)
使用Jquery Deferred
// 給出一段非常簡單的非同步操作程式碼,使用setTimeout函式
var wait = function(){
var task = function(){
console.log('執行完成)
}
setTimeout(task, 2000);
}
wait();
新增需求:要在執行完成之後進行某些特別複雜的操作,程式碼可能會很多,而且分好幾個步驟
function waitHandle(){
var dtd = $.Deferred(); // 建立一個deferred物件
var wait = function(dtd){ // 要求傳入一個deferred物件
var task = function(){
console.log('執行完成');
dtd.resolve(); // 表示非同步任務已經完成
// dtd.reject(); // 表示非同步任務失敗或出錯
}
setTimeout(task, 2000);
return dtd; // 要求返回deferred物件
}
// 注意,這裡一定要有返回值
return wait(dtd);
}
var w = waitHandle();
w.then(function(){
console.log('ok 1');
}, function(){
console.log('err 1');
}).then(function(){
console.log('ok 2');
}, function(){
console.log('err 2');
})
當執行dtd.reject()時:
var w = waitHandle();
w.then(function(){
console.log('ok 1');
}, function(){
console.log('err 1');
})
// 不能鏈式
w.then(function(){
console.log('ok 2');
}, function(){
console.log('err 2');
})
上面封裝的waitHandle
方法,由於直接返回了dtd
(deferred物件),所以使用者可以直接呼叫w.reject()
方法,導致無論是成功還是失敗,最後都走失敗。
// 修改
function waitHandle(){
var dtd = $.Deferred();
var wait = function(dtd){
var task = function(){
console.log('執行完成');
dtd.resolve();
}
setTimeout(task, 2000);
return dtd.promise(); // 注意這裡返回的是promise,而不是直接返回deferred物件
}
return wait(dtd);
}
ES6的Promise:點這裡
// promise封裝一個非同步載入圖片的方法
function loadImg(src) {
var promise = new Promise(function(resolve,reject){
var img = document.createElement('img');
img.onload = function(){
resolve(img)
}
img.onerror = function(){
reject('圖片載入失敗')
}
img.src = src;
})
return promise;
}
async/await
這是ES7提案中的,現在babel已經開始支援了,koa也是用async/await實現的。
- then 只是將callback拆分了
- async/await 是最直接的同步寫法
// 虛擬碼
const load = async function(){
const result1 = await loadImg(src1);
console.log(result1);
const result2 = await loadImg(src2);
console.log(result2);
}
load();
最後
建立了一個前端學習交流群,感興趣的朋友,一起來嗨呀!