JavaScript Promise查缺補漏

lvxfcjf發表於2021-09-09



Promise簡介

ECMAscript6原生提供了Promise物件,由瀏覽器直接支援,目前大多數瀏覽器都已經實現了,低版本瀏覽器可以使用es6-promise庫來填平相容性問題。Promise最大的好處是把執行程式碼和處理程式碼分離開,使非同步操作邏輯更加清晰。

Promise特點

1、物件的狀態不受外界影響
Promise物件代表一個非同步操作,有三種狀態:

  • pending - 初始狀態。

  • fulfilled - 操作成功完成。

  • rejected - 操作失敗。

只有非同步操作的結果可以決定當前是哪一種狀態,其他操作都不會影響狀態改變,這也是Promise最本質的特性,對於呼叫者的一種承諾

2、一旦狀態改變,就不會再變
Promise物件的狀態改變,只有兩種情況:從Pending變為Resolved和從Pending變為Rejected。只要這兩種情況發生,狀態就會固定,不會再變了,會一直保持這個結果,與事件不同的是,就算改變已經發生了,再對Promise物件新增回撥函式,也會立即得到這個結果,而事件一旦錯過再去監聽,就不會得到結果。

Promise優缺點

優點:

  • 有了Promise物件,就可以將非同步操作以同步操作的流程表達出來,避免了層層巢狀的回撥函式Promise

  • 物件提供統一的介面,使得控制非同步操作更加容易

缺點:

  • 無法取消 Promise,一旦新建它就會立即執行,無法中途取消

  • 如果不設定回撥函式,Promise內部丟擲的錯誤,不會反應到外部

  • 當處於Pending狀態時,無法得知目前進展到哪一個階段(剛剛開始或者即將完成)

Promise.prototype.then

Promise.prototype.then方法返回的是一個新的Promise物件,因此可以採用鏈式寫法

ajax("").then((json) => {    return json.post;
}).then((post) => {    // post處理});

程式碼中使用then方法,指定了兩個回撥函式。第一個回撥函式完成後,會將返回結果作為引數,傳入第二個then中的回撥函式執行。

ajax("").then((jsonURL) => {        return ajax(jsonURL);
}).then((post) => {        // post處理});

如果第一個回撥函式返回的是Promise物件,後一個回撥函式會等待該Promise物件的執行結果,等Promise執行結果返回,再進一步呼叫。
這種設計使得巢狀的非同步操作,可以被很容易得改寫,把回撥函式的"橫向發展"改為了"向下發展"。

Promise.prototype.catch

Promise.prototype.catch錯誤捕捉方法是Promise.prototype.then(null, rejection)的別名,用來指定發生錯誤時的處理函式。

ajax("").then((post) => {    throw Error();
}).catch((error) => {    // 捕捉回撥函式執行時發生的錯誤進行處理
    console.log('error:' + error);
});

Promise物件的錯誤具有"冒泡"性質,會一直向後傳遞,直到被捕獲為止。

ajax("").then((jsonURL) => {    return ajax(jsonURL);
}).then((comments) => {    throw Error();
}).catch((error) => {    // 處理前兩個回撥函式的錯誤
        console.log('error:' + error);
});

Promise.resolve

Promise.resolve方法可以將現有物件轉為Promise物件。如果Promise.resolve方法的引數,不是thenable 物件(具有then方法的物件 ),則返回一個新的Promise物件,且它的狀態為fulfilled。

const resolve = Promise.resolve('promise resolve');
resolve.then((s)=>{  console.log(s)
});

輸出結果:
promise resolve

如果Promise物件的例項狀態為fulfilled,回撥函式會立即執行,Promise.resolve方法的引數就是回撥函式的引數。
如果Promise.resolve方法的引數是一個Promise物件的例項,則會返回該Promise例項。

Promise.reject

Promise.reject(reason)方法與resolve方法類似,也會返回一個新的Promise例項,但該例項的狀態為rejected。Promise.reject方法的引數,會被傳遞給例項的回撥函式。

const reject = Promise.reject('promise reject');
reject.then(null, (err) => {  console.log(err)
});

輸出結果:
promise reject

Promise.all

Promise.all方法用於將多個Promise例項,包裝成一個新的Promise例項,該方法一般接受一個陣列作為引數,但不一定是陣列,只要具有iterator介面。且返回的每個成員都是Promise例項。

const getRandom = () => +(Math.random()*1000).toFixed(0);const ajax = (taskID) => new Promise(resolve => {    let timeout = getRandom();    console.log(`taskID=${taskID} start.`);
    setTimeout(function() {        console.log(`taskID=${taskID} finished in time=${timeout}.`);
        resolve(taskID)
    }, timeout);
});Promise.all([ajax(1),ajax(2),ajax(3)])
.then(resultList => {    console.log('results:',resultList);
});

輸出結果:
taskID=1 start.
taskID=2 start.
taskID=3 start.
taskID=2 finished in time=27.taskID=3 finished in time=257.taskID=1 finished in time=876.results: [1, 2, 3]

Promise.all狀態分為兩種:

  • 只有ajax(1)、ajax(2)、ajax(3)的狀態都變成fulfilled,返回的狀態才會變成fulfilled,此時ajax(1)、ajax(2)、ajax(3)的返回值組成一個陣列,傳遞給Promise.all的回撥函式。

  • 只要ajax(1)、ajax(2)、ajax(3)之中有一個是rejected,返回的狀態就變成rejected,此時第一個被reject的例項的返回值,會傳遞給Promise.all的回撥函式。

Promise.race

Promise.race方法也是將多個Promise例項,包裝成一個新的Promise例項,與Promise.all不同的是一旦有狀態改變,就會返回第一個狀態改變的Promise例項返回值。

const getRandom = () => +(Math.random()*1000).toFixed(0);const ajax = (taskID) => new Promise(resolve => {    let timeout = getRandom();    console.log(`taskID=${taskID} start.`);
    setTimeout(function() {        console.log(`taskID=${taskID} finished in time=${timeout}.`);
        resolve(taskID)
    }, timeout);
});Promise.race([ajax(1),ajax(2),ajax(3)])
.then(result => {    console.log('results:',result);
});

輸出結果:
taskID=1 start.
taskID=2 start.
taskID=3 start.
taskID=2 finished in time=59.results: 2taskID=3 finished in time=707.taskID=1 finished in time=854.

Promise.race雖然返回第一個狀態改變的Promise例項,但不能阻止其他Promise例項狀態改變。

如果Promise.all方法和Promise.race方法的引數,不是Promise例項,就會呼叫Promise.resolve方法,將引數轉為Promise例項,再進一步處理。



作者:bestvist
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3486/viewspace-2814149/,如需轉載,請註明出處,否則將追究法律責任。

相關文章