ES6(二):Promise

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。

ES6之前解決異步編程只能使用回調函數或事件,ES6中加入了 Promise,使得異步編程更加簡潔直觀和合理

特點

Promise是一個對象,具有以下兩個特點:

  • 對象的狀態不受外界影響
  • 狀態一旦改變就不會再變

使用方法

基本使用

ES6中規定,Promise對象是一個構造函數,於是我們就需要使用new關鍵字實例化:

code:

const promise = new Promise((resolve, reject) => {
    if (true) {
        resolve(successRes);
    } else {
        reject(errorText);
    }
});

Promise接受一個函數作爲參數,該函數的兩個參數分別是:resolvereject。其中:

resolve可以表示異步操作成功時調用 reject則可以表示異步操作失敗時調用

then

Promise實例生成之後,可以使用then方法分別指定成功和失敗狀態的回調函數。例如:

code

let a = 10;
const promise = new Promise((resolve, reject) => {
    if (a === 10) {
        resolve('成功!');
    } else {
        reject('失敗!');
    }
});
promise.then((res) => {
    console.log(res); // 成功!
}, (err) => {
    console.log(err);
});
let a = 0;
const promise = new Promise((resolve, reject) => {
    if (a === 10) {
        resolve('成功!');
    } else {
        reject('失敗!');
    }
});
promise.then((res) => {
    console.log(res);
}, (err) => {
    console.log(err); // 失敗!
});

當然,then的第二個參數並不是必須的,大部分時候我們其實都只需要第一個參數(成功),而失敗的回調可以放在catch中去執行。

catch

比如上面返回‘失敗’的例子,我們可以使用catch進行改造:

code

let a = 10;
const promise = new Promise((resolve, reject) => {
    if (a === 10) {
        resolve('成功!');
    } else {
        reject('失敗!');
    }
});
promise.then((res) => {
    console.log(res);
}).catch((err) => {
    console.log(err); //失敗!
});

大部分時候我們都是這樣使用的

Ajax實例

我們可以使用Promise對象實現一個ajax實例,這也是Promise用處最廣的地方:

code

const myAjax = function(data, url) {
    const promise = new Promise((resolve, reject) => {
        const stateChange = function() {
            if (this.readyState !== 4) {
                return;
            }
            if (this.status === 200) {
                resolve(this.response);
            } else {
                reject(new Error(this.statusText));
            }
        };
        const myHttp = new XMLHttpRequest();
        myHttp.open('GET', url, 'true');
        myHttp.onreadystatechange = stateChange;
        myHttp.responseType = 'json';
        myHttp.setRequestHeader('Accept', 'application/josn');
        myHttp.send(data);
    });
    return promise;
};

myAjax('/uuurl').then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
});

其他特性

新建就會立即執行

code

const promise = new Promise(function(resolve, reject) {
    console.log('我是resolve之前的打印');
    resolve();
});
  
promise.then(function() {
    console.log('我是成功的回調的打印');
});
  
console.log('我是最外層的打印');

// 結果:
// 我是resolve之前的打印
// 我是最外層的打印
// 我是成功的回調的打印

結果和我們想象的一致,先是打印resolveconsole,因爲Promise一建立就會執行,而後進行的是最外層的打印,那是因爲then指定的回調函數將在當前腳本所有同步任務執行完之後纔會執行。

一個異步操作的結果是返回另一個異步操作

通常情況下,reject函數的參數是Error對象的實例,表示拋出的錯誤;而resolve函數的參數除了正常的值以外,還可能是另一個Promise實例: code

const pro1 = new Promise((resolve, reject) => {

});
const pro2 = new Promise((resolve, reject) => {
    resolve(pro1);
});

上述代碼中,pro1pro2都是Promise實例,但是pro2resolvepro1作爲參數,此時pro1的狀態就會傳遞給pro2,也就是說,pro1的狀態決定了pro2的狀態。 這一點從以下代碼可以很直觀的看出來: code

const pro1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        return reject(new Error('err'));
    }, 3000);
});
const pro2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        return resolve(pro1);
    }, 1000);
});

pro2.then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
});

在這段代碼中,pro1是一個Promise對象,並且在3秒之後返回Error的實例errpro2的狀態則是在1秒之後改變。由於pro2返回的是另一個Promisepro1),導致pro2自己的狀態無效了,由pro1的狀態決定pro2的狀態。所以最終輸出的結果是:

過了3秒輸出Error:err

調用resolvereject並不會終結Promise的參數函數的執行

先來看一段代碼: code

const proromise = new Promise((resolve, reject) => {
    resolve('ok');
    console.log('我是resolve後面的代碼');
});

proromise.then(res => {
    console.log(res);
});

// 結果:
// 我是resolve後面的代碼
// ok

和預想的一樣,雖然先調用了resolve('ok');,但是後面的代碼還是會執行,並且會先打印出來,這是因爲調用resolvereject並不會終結Promise的參數函數的執行,而且then指定的回調函數將在當前腳本所有同步任務執行完之後纔會執行。一般調用resolvereject之後Promise的任務就完成了,所以建議在resolvereject之後加上return

參考鏈接

《ECMAScript 6 入門》——阮一峯 ECMAScript® 2015 Language Specification ECMAScript® 2016 Language Specification ECMAScript® 2019 Language Specification

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