JavaScript-Promise基礎知識

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方法裏面執行的,理清了這層,再來分析源碼會省力的多。

鏈接:

https://blog.csdn.net/shan1991fei/article/details/78966297 (ES6 Promise 用法)

Promise設計模式 (https://zhuanlan.zhihu.com/p/42377418 (30分鐘,讓你徹底明白Promise原理))

Promise的實現過程,其主要使用了設計模式中的觀察者模式:

1. 通過Promise.prototype.then和Promise.prototype.catch方法將觀察者方法註冊到被觀察者Promise對象中,同時返回一個新的Promise對象,以便可以鏈式調用。

2. 被觀察者管理內部pending、fulfilled和rejected的狀態轉變,同時通過構造函數中傳遞的resolve和reject方法以主動觸發狀態轉變和通知觀察者。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章