版權聲明:本文爲博主原創文章,未經博主允許不得轉載。
ES6之前解決異步編程只能使用回調函數或事件,ES6中加入了 Promise
,使得異步編程更加簡潔直觀和合理
特點
Promise
是一個對象,具有以下兩個特點:
- 對象的狀態不受外界影響
- 狀態一旦改變就不會再變
使用方法
基本使用
ES6中規定,Promise
對象是一個構造函數,於是我們就需要使用new
關鍵字實例化:
code:
const promise = new Promise((resolve, reject) => { if (true) { resolve(successRes); } else { reject(errorText); } });
Promise
接受一個函數作爲參數,該函數的兩個參數分別是:resolve
和reject
。其中:
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之前的打印 // 我是最外層的打印 // 我是成功的回調的打印
結果和我們想象的一致,先是打印resolve
的console
,因爲Promise
一建立就會執行,而後進行的是最外層的打印,那是因爲then
指定的回調函數將在當前腳本所有同步任務執行完之後纔會執行。
一個異步操作的結果是返回另一個異步操作
通常情況下,reject
函數的參數是Error
對象的實例,表示拋出的錯誤;而resolve
函數的參數除了正常的值以外,還可能是另一個Promise
實例:
code
const pro1 = new Promise((resolve, reject) => { }); const pro2 = new Promise((resolve, reject) => { resolve(pro1); });
上述代碼中,pro1
和pro2
都是Promise
實例,但是pro2
的resolve
將pro1
作爲參數,此時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
的實例err
。pro2
的狀態則是在1秒之後改變。由於pro2
返回的是另一個Promise
(pro1
),導致pro2
自己的狀態無效了,由pro1
的狀態決定pro2
的狀態。所以最終輸出的結果是:
過了3秒輸出
Error:err
調用resolve
或reject
並不會終結Promise
的參數函數的執行
先來看一段代碼: code
const proromise = new Promise((resolve, reject) => { resolve('ok'); console.log('我是resolve後面的代碼'); }); proromise.then(res => { console.log(res); }); // 結果: // 我是resolve後面的代碼 // ok
和預想的一樣,雖然先調用了resolve('ok');
,但是後面的代碼還是會執行,並且會先打印出來,這是因爲調用resolve
或reject
並不會終結Promise
的參數函數的執行,而且then
指定的回調函數將在當前腳本所有同步任務執行完之後纔會執行。一般調用resolve
和reject
之後Promise
的任務就完成了,所以建議在resolve
和reject
之後加上return
。
參考鏈接
《ECMAScript 6 入門》——阮一峯 ECMAScript® 2015 Language Specification ECMAScript® 2016 Language Specification ECMAScript® 2019 Language Specification