JS---Promise入門篇
這篇文章適合要接觸Promise的朋友們閱讀,裡面寫的是Promise中的一些基礎知識,並沒有涉及到太難理解的。
一、 初識Promise
1.1 什麼是Promise
Promise:ES6中新提供的一個內建類(提到類可以想到new、原型、原型鏈、例項。。。),基於promise可以有效的管理JS中的非同步程式設計,解決傳統非同步程式設計+回撥函式導致的‘回撥地獄’問題。
=> 我們把基於promise進行非同步管控的模式叫做‘promise設計模式’
1.2 學習Promise需要掌握的內容
從函式有三個角色入手:普通函式、建構函式(類)、普通物件
【普通物件 => 靜態的私有屬性方法】
promise。all()
promise。race()
promise。resolve()
promise。reject()
【類 => 原型鏈上的公共屬性和方法】
promise。prototype。then()
promise。prototype。catch()
promise。prototype。finally() [一般不用]
【new建立例項】
二、建立Promise例項
2.1 建立Promise例項的語法
語法:
let 例項 = new Promise([executor])
說明:
必須傳一個函式,否則會報錯:Uncaught TypeError: Promise resolver undefined is not a function
[executor]
是一個函式,我們一般在函式中管控我們的非同步程式設計程式碼
new Promise
的時候就會把
executor
立即執行
並且給
executor
函式傳遞兩個實參(兩個實參也都是函式):
resolve/reject
let p1 = new Promise(); //必須傳一個函式 Uncaught TypeError: Promise resolver undefined is not a function
let p2 = new Promise((resolve,reject)=>{
//非同步程式設計程式碼
});
2.2 Promise的兩個值
Promise的例項擁有[[PromiseStatus]]/[[PromiseValue]]
[[PromiseStatus]]是promise狀態(要麼是成功態要麼是失敗態)
準備狀態
pending
:
new Promise
的時候預設狀態就是
pending
成功狀態
fulfilled/resolved
:一般在非同步操作成功後,我們透過執行
resolved
函式,可以把
promise
的狀態改為
resolved
失敗狀態
rejected
:一般在非同步操作失敗後,我們透過執行
reject
函式,可以把
promise
的狀態改為
rejected
pending
=>
resolved / rejected
只要狀態一旦更改,則不可以再改變
[[PromiseValue]]是promise的值
不論執行
resolve/reject
哪個函式,都可以傳遞值,傳遞的值最後賦值給
[[PromiseValue]]
例如
: 我們建立一個Promise例項p1,建立例項的時候我們在函式中把他的resolved函式執行,也就是把它變為成功態
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(‘ok’);
console。log(p1) //=>‘resolve’ ‘ok’
}, 1000)
});
console。log(p1); //=>‘padding’ undefined
2.3 總結:建立例項時做的事情
我們在建立一個Promise的例項,需要給Promise傳遞一個函式,這個函式會立即執行,並且瀏覽器給它自帶兩個引數(兩個引數也都是函式),這兩個引數會改變Promise的狀態和值
三、Promise原型鏈上的公共方法---then
3.1 then是幹什麼的
我們在建立例項的時候可以修改Promise的狀態,目的就是為了控制then中的兩個方法,哪一個去執行。
then
方法:
例項。then([狀態成功時執行的],[狀態失敗時執行的])
result / reason
接收的是
[[PromiseValue]]
的資訊(在
executor
函式中,基於
resolve/reject
執行傳遞的值,就是給
promise-value
傳遞的值,並且只能傳遞一個值,傳遞第二個實參沒用)
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math。random() < 0。5) {
reject(‘NO’);
} else {
resolve(‘OK’);
}
}, 1000);
});
p1。then(result => {
console。log(`成功:${result}`);
}, reason => {
console。log(`失敗:${reason}`); //當上面的隨機是小於0。5的時候執行reject,也就是把狀態改為失敗態,從而執行then中的reason這個方法
});
3.2 then方法在什麼時候執行?
1. 建立例項時執行的是非同步程式碼:
非同步請求放置在
EXECUTOR
中,請求成功或者失敗後做啥事情都寫在
THEN
中
2. 建立例項時執行的不是非同步程式碼:
EXECUTOR
函式中理論上是管控非同步程式設計程式碼的,但是在開發中,你可以自己隨意處理;但是不論怎麼處理,
THEN
中的方法,只會在
PROMISE
狀態變為成功或者失敗的狀態下才會執行;
在
EXECUTOR
函式中執行
RESOLVE
或者
REJECT
,並不一定會立即通知
THEN
中的方法執行;如果在這兩個函式執行之前,已經基於
THEN
把成功或者失敗的方法放置好了,則立即通知執行;如果還沒有執行過
THEN
方法,則需要等到
THEN
執行後,方法放置好,再通知成功或者失敗的方法執行!
new Promise((resolve, reject) => {
// 非同步請求放置在EXECUTOR中,請求成功或者失敗後做啥事情都寫在THEN中
$。ajax({
url: ‘/api/info’,
method: ‘get’,
success: result => {
resolve(result);
},
error: reject
});
})。then(result => {
}, reason => {
});
new Promise((resolve, reject) => {
reject(100);
})。then(result => {
console。log(`成功:${result}`);
}, reason => {
console。log(`失敗:${reason}`);
});
3.3 then方法的返回值
每一次執行
。then
都會返回一個新的
Promise
例項(初始狀態:
pending
初值:
undefined
)。這樣可以繼續
。then
下去,這就是
Promise
中的
then
鏈機制
下面的例子說明:
p2這個例項的成功或者失敗狀態,是由
p1。then
這堆程式碼決定的
第一種情況:只要
p1。then
中不論哪個方法執行,
只要不報錯
,新的p2例項的狀態都會變為
成功態
,而方法返回的結果就是p2例項的
promise-value
值(也就是上一個
then
執行的返回結果,會傳遞給下一個
then
中的方法);同理,兩個方法中,
不管哪一個執行報錯,P2一定是失敗態
第二種情況:如果
p1。then
中的某個方法執行,返回的是新的
Promise
例項,則會等待這個
Promise
的執行結果,作為p2的執行狀態
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math。random() < 0。5) {
reject(‘NO’);
} else {
resolve(‘OK’);
}
}, 1000);
});
let p2 = p1。then(result=>{
},reason=>{
});
console。log(p2);
let p3 = p2。then(result => {
console。log(result); //=>‘OK@@’
// return Promise。resolve(100); //=>P3的狀態變為成功,值是100
return Promise。reject(0); //=>P3的狀態變為失敗,值是0
}, reason => {
});
p3。then(result => {
console。log(‘成功’ + result);
}, reason => {
console。log(‘失敗’ + reason);
});
3.4 then鏈的理解
下面程式碼的輸出結果:把這段程式碼理解會then就掌握差不多了(鏈有點長 ,不著急,下面搭配了註釋 )
new Promise(resolve => {
setTimeout(() => {
resolve(10); //1000MS後 第一個PROMISE例項狀態是成功 VALUE:10
}, 1000);
})。then(result => {
console。log(`成功:${result}`); //=>‘成功:10’
return result * n; //報錯:ReferenceError: n is not defined 也就是讓此次THEN返回的PROMISE例項變為失敗態,VALUE:失敗的原因
}, reason => {
console。log(`失敗:${reason}`);
return reason * 10;
})。then(result => {
console。log(`成功:${result}`);
return result * 20;
}, reason => {
console。log(`失敗:${reason}`); //=>‘失敗: ReferenceError: n is not defined’
return reason * 10; //NaN 程式碼執行沒有報錯,讓當前THEN返回的例項狀態:成功 VALUE:NAN
})。then(result => {
console。log(`成功:${result}`); //=>‘成功:NaN’
return result * 20; //NaN 程式碼執行沒有報錯,讓當前THEN返回的例項狀態:成功 VALUE:NAN
}, reason => {
console。log(`失敗:${reason}`);
return reason * 10;
})。then(result => {
console。log(`成功:${result}`); //=>‘成功:NaN’
return Promise。reject(result * 20); //返回的新的失敗的PROMISE例項影響了本次THEN返回PROMISE例項的狀態和結果(和RETURN的保持一致)
}, reason => {
console。log(`失敗:${reason}`);
return Promise。resolve(reason * 10);
})。then(result => {
console。log(`成功:${result}`);
}, reason => {
console。log(`失敗:${reason}`); //=>‘失敗:NaN’
});
3.5 then中只寫一個方法的理解
在
THEN
中只設置一個方法,成功或者失敗執行的方法沒有設定,會順延到下一個
THEN
中查詢(依然是按照當前的狀態查詢,例如:失敗的狀態,第一個
THEN
沒有設定失敗的方法,會找第二個
THEN
中設定的失敗的方法。。。)
比如麼有設定
reason
,那麼需要執行它的時候預設做的是什麼事情呢?
=> { 不寫的情況下,
PROMISE
內部預設補充了一下(實現的效果:繼續找下一個
THEN
中代表失敗的)}
=> 即執行的是這句程式碼(因為是執行的失敗態,所以返回的例項是失敗態):
return Promise。reject(reason);
說了這麼多,到底理解不理解 ,哈哈上程式碼 (內心是拒絕的,因為它肯定也會是很長的then鏈)
new Promise((resolve, reject) => {
setTimeout(() => {
reject(10); //狀態:失敗 值:10
}, 1000);
})。then(result => {
console。log(`成功:${result}`);
return result * 10;
})。then(result => {
console。log(`成功:${result}`);
return result * 10;
})。then(null, reason => {
console。log(`失敗:${reason}`); //=>‘失敗:10’
return reason * 2;
//程式碼執行沒有報錯,讓當前例項狀態:成功 值:20
})。then(null,reason => {
console。log(`失敗:${reason}`);
return reason * 2;
})。then(result => {
console。log(`成功:${result}`); //=>‘成功:20’
});
四、Promise.all()
Promise。all([PROMISE1,PROMISE2,。。。])
:等待所有的
PROMISE
例項都成功,整體才是成功的(返回新的
PROMISE
例項),只要有一個例項是失敗的,整體例項就是失敗的;
function fn1() {
return new Promise(resolve => {
setTimeout(_ => {
resolve(10);
}, 2000);
});
}
function fn2() {
return Promise。resolve(20);
}
function fn3() {
return new Promise(resolve => {
setTimeout(_ => {
resolve(30);
}, 500);
});
}
Promise。all([fn1(), fn2(), fn3()])。then(results => {
// 只有當三個PROMISE例項都成功的時候(等待最晚有結果的一個也是成功),才會觸發執行,results會按照放置的順序,儲存著每一次獲取的結果
console。log(results); //=>[10, 20, 30]
});
五、Promise.race()
Promise。race()
同時傳送多個請求,誰先有處理結果(不管結果是成功還是失敗),就以誰的結果為主(哪怕是失敗的)
六、【補充】:異常捕獲
6.1 catch表示then 中只寫一個reason方法
在專案中,我們會用
CATCH(REASON=>{})
代替
THEN(NULL,REASON=>{})
,效果是一模一樣的(執行
CATCH
也會返回新的
PROMISE
例項,裡面設定的方法是在例項為失敗狀態下執行的)
=>在專案中,我們一般
THEN
中放的是成功執行的,
CATCH
中放的是失敗執行的
new Promise((resolve, reject) => {
setTimeout(() => {
reject(10);
}, 1000);
})。then(result => {
console。log(`成功:${result}`);
return result * 10;
})。catch(reason => {
console。log(`失敗:${reason}`);
return reason * 2;
});
6.2 異常捕獲:try catch
為什麼要用try catch?
比如下面的程式碼:我們輸出一個從來沒有定義過的變數,會報錯,下面的程式碼也不再執行,但是我們想讓下面的程式碼繼續執行,就需要用到
try catch
console。log(n); //=>Uncaught ReferenceError: n is not defined 瀏覽器丟擲異常資訊,下面程式碼就不會再執行了
console。log(‘OK’);
try catch
try
:把可能會報錯的程式碼放置到
try
中捕獲異常(程式碼執行一但報錯,控制檯是不丟擲異常的,不會影響後續程式碼的執行)
catch
:
catch
中捕獲到異常資訊 (可以把資訊上報給伺服器)
try {
// 把可能會報錯的程式碼放置到TRY中捕獲異常(程式碼執行一但報錯,控制檯是不丟擲異常的,不會影響後續程式碼的執行)
console。log(n);
} catch (error) {
// CATCH中捕獲到異常資訊 (可以把資訊上報給伺服器)
// console。log(error);
}
console。log(‘OK’);