原型
異步
一、什麼是單線程,和異步有什麼關係
單線程:只有一個線程,同一時間只能做一件事
原因:避免DOM渲染的衝突
解決方案:異步
爲什麼js只有一個線程:避免DOM渲染衝突
- 瀏覽器需要渲染DOM
- JS可以修改DOM結構
- JS執行的時候,瀏覽器DOM渲染會暫停
- 兩端JS也不能同時執行(都修改DOM就衝突了)
- webworker支持多線程,但是不能訪問DOM
解決方案存在的問題
- 問題一:沒按照書寫方式執行,可讀性差
- 問題二:callback中不容易模塊化
二、什麼是event-loop
- 事件輪詢,JS實現異步的具體解決方案
- 同步代碼,直接執行
- 異步函數先放在異步隊列中
- 待同步函數執行完畢,輪詢執行異步隊列的函數
setTimeout(function(){
console.log(1);
},100); //100ms之後才放入異步隊列中,目前異步隊列是空的
setTimeout(function(){
console.log(2); //直接放入異步隊列
})
console.log(3) //直接執行
//執行3之後,異步隊列中只有2,把2拿到主線程執行,2執行完之後,異步隊列中並沒有任務,所以一直輪詢異步隊列,直到100ms之後1放入異步隊列,將1拿到主線程中執行
$.ajax({
url:'./data.json',
success:function(){ //網絡請求成功就把success放入異步隊列
console.log('a');
}
})
setTimeout(function(){
console.log('b')
},100)
setTimeout(function(){
console.log('c');
})
console.log('d')
//打印結果:
//d //d
//c //c
//a //b
//b //a
//真實環境不會出現dacb
三、是否用過jQuery的Deferred
- jQuery1.5的變化
- 使用jQuery Deferred
- 初步引入Promise概念
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對象
jQuery1.5之後
var ajax = $.ajax('./data.json');
ajax.done(function(){
console.log('success1')
})
.fai(function(){
console.log('fail')
})
.done(function(){
console.log('success2');
})
console.log(ajax); //deferred對象
var ajax = $.ajax('./data.json');
ajax.then(function(){
console.log('success1')
},function(){
console.log('error1');
})
.then(function(){
console.log('success2');
},function(){
console.log('error');
})
//使用
var w = waithandle()
w.then(function(){
console.log('ok1');
},function(){
console.log('err2');
})
.then(function(){
console.log('ok2');
},function(){
console.log('err2');
})
//還有w.wait w.fail
- 無法改變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;
}
//注意,這裏已經要有返回值
return wait(dtd);
}
/*
*總結:dtd的API可分成兩類,用意不同
*第一類:dtd.resolve dtd.reject
*第二類:dtd.then dtd.done dtd.fail
*這兩類應該分開,否則後果嚴重!
*可以在上面代碼中最後執行dtd.reject()試一下後果
*/
使用dtd.promise()
function waitHandle(){
var dtd = $.Deferred();
var wait = function(){
var task = function(){
console.log('執行完成');
dtd.resolve();
}
setTimeout(task,2000)
return dtd.promise(); //注意這裏返回的是promise,而不是直接返回deferred對象
}
return wait(dtd)
}
var w = waitHandle(); //promise對象
$.when(w).then(function(){
console.log('ok1');
},function(){
console.log('err1');
})
/*
只能被動監聽,不能干預promise的成功和失敗
*/
- 可以jQuery1.5對ajax的改變舉例
- 說明如何簡單的封裝、使用deferred
- 說明promise和Defrred的區別
要想深入瞭解它,就需要知道它的前世今生
四、Promise的基本使用和原理
基本語法回顧
異常捕獲
//規定:then只接受一個函數,最後統一用catch捕獲異常
多個串聯
var scr1 = 'https://www.imooc.com/static/img/index/logo_new.png';
var result1 = loadImg(src1);
var src2 = 'https://www.imooc.com/static/img/index/logo_new.png';
var result2 = loadImg(src2);
result1.then(function(img1) {
console.log('第一個圖片加載完成', img1.width);
return result2;
}).then(function(img2) {
console.log('第二個圖片加載完成', img2.width);
}).catch(function(ex) {
console.log(ex);
})
Promise.all和Promise.race
//Promise.all接收一個promise對象的數組
//待全部完成後,統一執行success
Promise.all([result1, result2]).then(datas => {
//接收到的datas是一個數組,依次包含了多個promise返回的內容
console.log(datas[0]);
console.log(datas[1]);
})
//Promise.race接收一個包含多個promise對象的數組
//只要有一個完成,就執行success
Promise.race([result1, result2]).then(data => {
//data即最先執行完成的promise的返回值
console.log(data);
})
Promise標準
- 三種狀態:pending,fulfilled,rejected
- 初始狀態:pending
- pending變爲fulfilled,或者pending變爲rejected
- 狀態變化不可逆
promise必須實現then這個方法
then()必須接收兩個函數作爲標準
then
五、介紹一下async/await(和Promise的區別、聯繫)
六、總結一下當前JS結局異步的方案
虛擬DOM
MVVM和vue
組件化和React
hybrid
未完待續,每日更新