Promise是ES6中新增的一個類,專門用來解決異步回調地獄的問題,異步代碼同步顯示出來。
回調函數的例子
//成功回調函數
function successCallback() { // 固定格式
console.log('SUCCESS')
}
//失敗回調函數
function failureCallback() { // 固定格式
console.log('FAILURE')
}
function myFunc(param, success, failure) {
if (param) {
success()
} else {
failure()
}
}
myFunc(0, successCallback, failureCallback) // FAILURE
myFunc(1, successCallback, failureCallback) // SUCCESS
以上回調函數可用promise實作
var t = 0;
var promise = new Promise(function(resolve, reject) {
if (t%2 == 0) {
resolve();
} else {
reject();
}
});
promise.then(function() {
console.log('SUCCESS');
}, function(){
console.log('FAILURE');
});
運作結果:
SUCCESS
注:new了一個Promise對象,沒調用它,函數就已經執行了。是以使用Promise時一般包在一個函數中,在需要時調用。
是以,上面的執行個體可以包在函數fn()裡面,需要時就調用fn()
function fn() {
var t = 1;
var promise = new Promise(function(resolve, reject) {
if (t%2 == 0) {
console.log(1);
resolve();
console.log(1.1);
} else {
console.log(2);
reject();
console.log(2.1);
}
});
promise.then(function() {
console.log(3);
console.log('SUCCESS');
console.log(3.1);
}, function(){
console.log(4);
console.log('FAILURE');
console.log(4.1);
});
return promise;
}
fn();
運作結果:
2
2.1
4
FAILURE
4.1
Promise運作流程

function test_01() {
new Promise(function(resolve, reject) {
setTimeout(() => {
resolve('success....'),
reject('fail...')
}, 1000);
}).then(
value => {
console.log('onResolved()1', value);
}
).catch(
reason => {
console.log('onRejected()1', reason);
}
)
}
test_01();
輸出結果:onResolved()1 success....
setTimeout中resolve/reject隻能執行一個,要麼成功、要麼失敗。
function test_02() {
// 産生一個成功值為1的promise對象
const p1 = new Promise(function(resolve, reject) {
resolve(1)
})
// 産生一個成功值為2的promise對象
const p2 = Promise.resolve(2);
// 産生一個失敗值為3的promise對象
const p3 = Promise.reject(3);
p1.then(value => {console.log(value)})
p2.then(value => {console.log(value)})
// p3.then(null, reason => {console.log(reason)}) // 寫法1
// p3.then(null, value => {console.log(value)}) // 寫法2
// p3.catch(value => {console.log(value)}) // 寫法3
p3.catch(reason => {console.log(reason)}) // 寫法4
}
test_02();
輸出結果:1 2 3
function test_01() {
new Promise(resolve => {
console.log(123);
}).then(value => {console.log(456)})
console.log("hi")
}
test_01();
執行結果:123 hi
注:then沒有被執行,是因為沒有resolve通知。隻有得到通知才會進入微任務隊列。
Promise異常捕獲
Promise.prototype.catch方法是Promise.prototype.then(null, rejection)的别名,用于指定發生錯誤時的回調函數。
Promise對象的錯誤具有“冒泡”性質,會一直向後傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch語句捕獲。
getJSON("/visa.json").then(function(json) {
return json.name;
}).then(function(name) {
// proceed
}).catch(function(error) {
//處理前面任一個then函數抛出的錯誤
});
<script>
function test_01() {
const p = new Promise(function(resolve, reject) {
setTimeout(() => {
// resolve('success....'), // promise變為resolved 成功狀态
// reject('fail...') // promise變為rejected 失敗狀态
throw new Error('error....') // 抛出異常,promise變為pending狀态,value為'error...'
}, 1000);
});
console.log(p);
}
test_01();
</script>
先改狀态,還是先改值?
function test_03() { // 正常:先執行回調函數,後改狀态
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1); // 2. 改變狀态(pending → resolved, 同時調用回調函數指定資料)setTimeout為宏任務
},1000)
}).then( // 1. 執行回調函數(微任務),儲存目前指定的回調函數
value => {},
reason => {}
)
}
function test_04() {// 先改狀态,後執行回調函數
new Promise((resolve, reject) => {
resolve(1); // 1. 改變狀态(pending → resolved, 同時調用回調函數指定資料)
}).then( // 2. 後執行回調函數(微任務),異步執行回調函數
value => {},
reason => {}
)
}
Promise狀态改變是不可逆的,pending → resolved之後,無法再轉為其他狀态。
是以,resolve / reject 隻能執行一個。
Promise鍊式調用
- 回調地獄
- 代碼難閱讀
- 多個串聯函數适合使用回調函數嵌套
鍊式Promise是指在目前promise達到fulfilled狀态後,即開始進行下一個promise(後鄰promise)。如何銜接目前promise和後鄰promise呢?(這是這裡的難點)。隻要在then方法裡面return一個promise就好了(得到一個promise是前提)。
注意: 一定要有傳回值,否則,callback 将無法擷取上一個 Promise 的結果。
(如果使用箭頭函數,() => x 比 () => { return x; } 更簡潔一些,但後一種保留 return 的寫法才支援使用多個語句。)。
function runAsync1(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('異步任務1執行完成');
resolve('随便什麼資料1');
}, 1000);
});
return p;
}
function runAsync2(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('異步任務2執行完成');
resolve('随便什麼資料2');
}, 2000);
});
return p;
}
function runAsync3(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('異步任務3執行完成');
resolve('随便什麼資料3');
}, 2000);
});
return p;
}
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){
console.log(data);
});
運作結果:
異步任務1執行完成
随便什麼資料1
異步任務2執行完成
随便什麼資料2
異步任務3執行完成
随便什麼資料3
如果then不傳回Promise對象,則後面then不再受到資料。
function runAsync1(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('異步任務1執行完成');
resolve('随便什麼資料1');
}, 1000);
});
return p;
}
function runAsync2(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('異步任務2執行完成');
resolve('随便什麼資料2');
}, 2000);
});
return p;
}
function runAsync3(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('異步任務3執行完成');
resolve('随便什麼資料3');
}, 2000);
});
return p;
}
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return '直接傳回資料'; //這裡直接傳回資料
})
.then(function(data){
console.log(data);
});
運作結果:
異步任務1執行完成
随便什麼資料1
異步任務2執行完成
随便什麼資料2
直接傳回資料
promise裡面的then函數僅僅是注冊了後續需要執行的代碼,真正的執行是在resolve方法裡面執行的,理清了這層,再來分析源碼會省力的多。
連結:
(ES6 Promise 用法)
Promise設計模式 (https://zhuanlan.zhihu.com/p/42377418 (30分鐘,讓你徹底明白Promise原理))
Promise的實作過程,其主要使用了設計模式中的觀察者模式: