Promise
promise就是個構造函數,參數為一個函數
構造函數一般使用其執行個體進行操作,如何得到一個構造函數的執行個體呢?
1、new Promise(參數:函數);
2、參數為一個函數,這個函數同樣也有兩個參數 resolve reject,這兩個參數也是函數
resolve執行後傳回promise的成功狀态,
reject執行後傳回promise的錯誤狀态
基本使用形式:
var p = new Promise(function(resolve,reject){
console.log("結果");
resolve("結果")
})
//以上代碼将一段同步代碼封裝成了一個promise執行個體,promise執行個體有兩個三個狀态
//peding 正在請求
//resolved 成功
//rejected 失敗
//這個執行個體内部調用resolve,傳回成功狀态,并将結果通過resolve傳遞。
//傳遞出來的結果如何使用呢?
//下面的代碼,通過調用promise執行個體上面的then方法來獲得resolve傳遞的資料。
p.then(function(data){
console.log(data)
})
從上面的代碼可以總結一下promise的特點:
- 隻要一 new ,promise就執行了,并且得到一個promis執行個體
- promise執行個體内部結果通過resolve進行傳遞
- 通過調用promise執行個體的then方法擷取resolve傳遞的結果;
一般用promise幹什麼呢?
包裝異步執行代碼,将其轉化為同步執行的樣式,
比方發送一個ajax正常方法:
$.get("https://cnodejs.org/api/v1/topics?tab=share",function(data){
console.log(data);
})
轉化為Promise如下:
let p = new Promise((resolve,reject)=>{
$.get("https://cnodejs.org/api/v1/topics?tab=share",function(data){
resolve(data)
})
});
p.then((data)=>{
//執行代碼邏輯
console.log(data)
})
在實際開發中使用
但是一般在開發中一般不會把new Promise直接暴露在外面,而是封裝成一個函數,上面代碼修改如下:
function fn(){
let p = new Promise((resolve,reject)=>{
$.get("https://cnodejs.org/api/v1/topics?tab=share",function(data){
resolve(data)
})
});
return p
}
fn().then((data)=>{
console.log(data)
})
如何學習promise???
根據作用需求/場景來學習,需求如下:
比方一個頁面加載進來,需要發送5個ajax請求,并需要把結果統一經行處理:
-
首先大家按照大家已有的知識點來思考一下如何解決呢?
代碼如下:
$(function(){
var arr = []
$.get("https://cnodejs.org/api/v1/topics?tab=ask",function(data){
arr.push(data);
$.get("https://cnodejs.org/api/v1/topics?tab=job",function(data){
arr.push(data);
$.get("https://cnodejs.org/api/v1/topics?tab=good",function(data){
arr.push(data);
$.get("https://cnodejs.org/api/v1/topics?tab=share",function(data){
arr.push(data);
console.log(arr)
})
})
})
})
})
簡稱 回調地獄
缺點耗費時間,看圖說話:
之前我們可能會用解決方法。
(function () {
var count = 0;
var arr = [];
function handle() {
if (count === 4) {
console.log(arr);
}
}
$.get("https://cnodejs.org/api/v1/topics?tab=good",function(data){
arr.push(data);
count++;
handle()
})
$.get("https://cnodejs.org/api/v1/topics?tab=job",function(data){
arr.push(data);
count++;
handle()
})
$.get("https://cnodejs.org/api/v1/topics?tab=share",function(data){
arr.push(data);
count++;
handle()
});
$.get("https://cnodejs.org/api/v1/topics?tab=ask",function(data){
arr.push(data);
count++;
handle()
});
})();
但是這種依然有個缺點,得寫監控函數,每次回調都會調用監控函數,耗費性能,還有其他方法嗎?
檢視network 中的waterfall;
Promise 來了,用promise怎麼實作呢?
代碼如下:
$(function(){
// 封裝一個promise;将url提取出來;
var p = function(url){
return new Promise(function(resolve,reject){
$.get(url,function(data){
resolve(data);
})
})
}
Promise.all([
p("https://cnodejs.org/api/v1/topics?tab=good"),
p("https://cnodejs.org/api/v1/topics?tab=share"),
p("https://cnodejs.org/api/v1/topics?tab=ask"),
p("https://cnodejs.org/api/v1/topics?tab=job"),
]).then(function(result){
console.log(result);
})
})
waterfall圖如下:
有的時候有這樣的需求,後面的ajax請求依賴前面的ajax請求必須按照順序調動如何實作呢?
前面的回調是一種解決方案,但是想避免回調地獄的寫法用promise實作順序調用呢?
$(function(){
// 封裝一個promise;
var p = function(url){
return new Promise(function(resolve,reject){
$.get(url,function(data){
resolve(data);
})
})
}
var arr = []
p("https://cnodejs.org/api/v1/topics?tab=ask")
.then(function(data){
arr.push(data);
return p("https://cnodejs.org/api/v1/topics?tab=share")
}).then(function(data){
arr.push(data);
return p("https://cnodejs.org/api/v1/topics?tab=ask")
}).then(function(data){
arr.push(data);
return p("https://cnodejs.org/api/v1/topics?tab=good")
}).then(function(data){
arr.push(data);
console.log(arr);
})
})
waterfall執行圖:
再看一個應用場景:
請求多個資源,哪個接口先傳回,就處理哪個接口的資訊:
一般應用的比較多的是分布式應用,舉例子說明,
四台 伺服器分别位于 北京 上海 南京 鄭州:畫圖說明
這應用到一個race接口,将4.html的js代碼改成如下:
$(function(){
// 封裝一個promise;
var p = function(url){
return new Promise(function(resolve,reject){
$.get(url,function(data){
resolve(data);
})
})
}
Promise.race([
p("https://cnodejs.org/api/v1/topics?tab=good"),
p("https://cnodejs.org/api/v1/topics?tab=share"),
p("https://cnodejs.org/api/v1/topics?tab=ask"),
p("https://cnodejs.org/api/v1/topics?tab=job"),
]).then(function(result){
console.log(result);
})
})
運作看結果,誰最先傳回就列印誰的資料:
再來看一個需求,發送一個請求,請求逾時後傳回特定資訊如何實作:
jquery版本:
$.ajax({
url:'https://cnodejs.org/api/v1/topics?tab=good', //請求的URL
timeout : 1000, //逾時時間設定,機關毫秒
type : 'get', //請求方式,get或post
data :{}, //請求所傳參數,json格式
dataType:'json',//傳回的資料格式
success:function(data){ //請求成功的回調函數
alert("成功");
},
error:function(){
console.log("逾時了")
}
});
//設定timeout的時間,逾時後觸發error函數。
Promise版本如何實作呢?
- 封裝兩個Promise
- 一個promise裡面封裝ajax請求
- 一個Promise封裝定時器
- 調用Promise類的race方法
- 鍊式調用then方法獲得執行結果。
注意:Promise.race的參數為一個數組,數組每一項都是promise執行個體對象。
Promise.race的傳回結果為一個Prmise執行個體,隻不過這個執行個體隻能是最先執行resolve的那個promise。
$(function(){
// 封裝一個promise;
var p = function(url){
return new Promise(function(resolve,reject){
$.get(url,function(data){
resolve(data);
})
})
}
var p2 = function(){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve('請求逾時了')
},100)
})
}
Promise.race([
p("https://cnodejs.org/api/v1/topics?tab=good"),
p2()
]).then(function(result){
console.log(result);
})
})
promise捕獲錯誤的使用.
如何捕獲promise的錯誤呢?在promise的調用鍊的最後調用catch函數,一旦promise調用鍊中有reject執行,promise就會終止執行直接進入catch函數,否則catch不會執行。
$(function(){
// 封裝一個promise;
var p = function(url){
return new Promise(function(resolve,reject){
$.get(url,function(data){
resolve(data);
})
})
}
// P2的另一種形态:
// 如果是reject會直接跳到catch裡面去,如果是resolve,會接着執行,不會跳躍
var p2 = function(){
return new Promise(function(resolve,reject){
setTimeout(function(){
reject("err");
},100)
})
}
Promise.race([
p("https://cnodejs.org/api/v1/topics?tab=good"),
p2()
]).then(function(result){
console.log(result);
}).then(function(){
console.log("我運作了")
}).catch(function(data){
console.log(data);
})
})
javascript的宏任務和微任務
看一道面試題:
setTimeout(
function(){
console.log('1')
}
);
new Promise(
function(resolve, reject){
resolve()
console.log('2');
}).then(function(){
console.log('3')
});
console.log('4');
請問這段代碼的執行結果是什麼?
回憶javascript的異步機制,如圖:
這張圖不是很完整,在javascript中分為宏任務(macrotask)和微任務(micro-task);
宏任務(macrotask)包括 cript(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering
micro-task: process.nextTick, Promises(這裡指浏覽器實作的原生 Promise)
;