對寫文章這件事已經閣了3個月了,工做太忙很難抽出大塊時間來總結寫做。如今稍微閒賦些,準備好好對之前的技術作一下總結。爲何要寫關於 Promise
呢?有如下 3 點ios
以前面試時候,面試官問 Promise
問的比較多,實在要對它進行一個好好總結了,也是 JavaScript 比較難懂的一個技術點,平時我面試別人時候,也喜歡問 Promise
相關的,保證他在工做中可以熟練運用,本身能封裝axios
,能控制比較複雜的同步異步流程等...es6
這三個月的工做中,本身也老是遇到關於 Promise
的一些運用場景,有時候又比較疑惑一些地方,也是爲本身總結一下 Promise
,之後在工做中更加運用自如。面試
接下來我從最基礎的同步異步談起,再經過圖示談他的語法,如何本身寫一個Promise
,再談一下我在工做中遇到的 Promise
,最後談一下 新的語法 async
和 await
與 Promise
的結合和比較axios
網上關於Promise
的介紹大多數是一些語法的介紹,大多沒有結合場景和同步異步相關理解來談。對於理解Promise
的前世此生仍是不夠深刻,不夠具體。數組
console.log(1)
setTimeout(() => {
console.log(2)
}, 1000)
console.log(3)
複製代碼
若是延遲時間爲
0
呢
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
console.log(3)
複製代碼
console.log(1)
setTimeout(() => {
console.log(2)
}, 2000)
console.log(3)
setTimeout(() => {
console.log(4)
}, 1000)
console.log(5)
setTimeout(() => {
console.log(6)
}, 0)
console.log(7)
setTimeout(() => {
console.log(8)
}, 1000)
console.log(9)
複製代碼
這裏能夠發現幾點promise
這裏咱們總結下同步與異步的規律瀏覽器
這是一個怎樣的機制,JS引擎又是如何處理的呢?異步
我找到了一張這樣的圖async
首先來解釋一下這個圖:這是JS的事件循環,分爲3個步驟函數
如何處理異步呢, 最初的方法是使用回調
引入一下知乎上的高贊回答
如今咱們把它用代碼寫出來看看function fetchSomething() {
console.log('去取貨!')
}
function buySomething(callback) {
console.log('沒貨了!')
setTimeout(() => {
console.log('有貨了!')
callback()
}, 1000)
}
buySomething(fetchSomething)
複製代碼
用簡單的語言描述就是:
將一個函數做爲參數傳給另外一個函數調用
要搞清楚的一點是,回調和異步沒有直接的關係,也能夠同步回調,也能夠異步回調。咱們是經過回調這個機制來實現異步的操做,例如
// 這個一個請求的異步函數,來實現異步操做
function getSomePeopleName(params, callback) {
setTimeout(() => {
let data
if (params === 'suo') {
data = 'yue'
}
console.log(callback(data))
}, 1000)
}
console.log(getSomePeopleName('suo', (data) => '我是回調函數:' + data))
複製代碼
回調函數的結果必定是在回調函數裏面,若是我是這樣一個流程呢?
A -> B -> C -> D
function A(callback) {
console.log('開始執行A')
setTimeout(() => {
callback('A')
}, 500)
}
function B(callback) {
console.log('開始執行B')
setTimeout(() => {
callback('B')
}, 400)
}
function C(callback) {
console.log('開始執行C')
callback('C')
}
function D(callback) {
console.log('開始執行D')
setTimeout(() => {
callback('D')
}, 200)
}
A((a) => {
B((b) => {
C((c) => {
D((d) => {
console.log(a, b, c, d)
})
})
})
})
複製代碼
由上可知,回調函數有幾個特定
那麼有沒有更好的異步操做機制呢?這裏咱們就要談到 Promise
了
Promise
是一個構造函數咱們寫一個簡單的 Promise
看看
new Promise(() => {})
複製代碼
// 有一個參數executor的構造函數
// executor 也是一個函數,具備兩個參數 resolve, reject 是兩個回調函數,當執行到回調函數時,會執行
const isResolve = true
new Promise((resolve, reject) => {
if (isResolve) {
resolve()
} else {
reject()
}
})
複製代碼
能夠看到
Promise
的狀態從
pending
變成
resolved
若是將 isResolve
置爲 false
呢?
Promise
狀態從
pending
變成
rejected
同時拋出一個異常,而且異常未被捕獲,因此咱們寫Promise時候必定要加上catch 來捕獲異常
const isResolve = false
new Promise((resolve, reject) => {
if (isResolve) {
resolve()
} else {
reject('我拒絕你')
}
}).catch((err) => {
console.log('捕獲異常', err)
})
複製代碼
如今咱們把異常捕獲到了,可是神奇的事情發生了,異常狀態應該是
rejected
,怎麼變成
resolved
了呢? 咱們或許很納悶,這個放在後面討論,咱們先看看
then
怎麼處理的
const isResolve = true
new Promise((resolve, reject) => {
if (isResolve) {
resolve('經過')
} else {
reject('我拒絕你')
}
}).then((res) => {
console.log('resolve', res)
}, (res) => {
console.log('reject', res)
})
複製代碼
當
isResolve = false
時
咱們能夠看到
then
是怎麼處理的
resolve()
調用,Promise狀態爲 resolved
,then
則執行第一個回調函數參數reject()
調用,Promise狀態爲 rejected
,then
則執行第二個回調函數參數下面進一步驗證下
const isResolve = false
new Promise((resolve, reject) => {
if (isResolve) {
resolve('經過')
} else {
reject('我拒絕你')
}
}).catch(err => {
console.log('我捕獲到了', err)
}).then((res) => {
console.log('resolve', res)
}, (res) => {
console.log('reject', res)
})
複製代碼
果真呢,
catch
以後,
then
還會去執行,而且
resolve
的參數爲
undefined
問題是如今咱們如何控制一個流程呢?Promise
並不能直接給咱們進行長流程的分支選擇
下面有一個簡單流程
嘗試使用Promise
去控制流程
const process = [102, 204]
new Promise((resolve, reject) => {
if (process[0] === 101) {
setTimeout(() => {
console.log(101)
resolve(101)
}, 1000)
} else {
setTimeout(() => {
console.log(102)
reject(102)
}, 1000)
}
}).then(() => {
if (process[1] === 201) {
setTimeout(() => {
console.log(201)
}, 500)
} else {
setTimeout(() => {
console.log(202)
}, 500)
}
}, () => {
if (process[1] === 203) {
setTimeout(() => {
console.log(203)
}, 500)
} else {
setTimeout(() => {
console.log(204)
}, 500)
}
}).catch((err) => {
console.log(err)
})
複製代碼
下面咱們試試更加複雜的流程
單個Promise
沒辦法控制長流程,咱們怎麼將Promise
造成一個控制鏈呢,須要理解then
的返回在其中起到的做用
promise.then(onFulfilled, onRejected)
複製代碼
接收兩個參數,onFulfilled
在promise
的 resolved
狀態被調用
接收兩個參數,onRejected
在promise
的 rejected
狀態被調用
返回值比較複雜,下面用表格列出來
序號 | 場景 | 返回的Promise狀態改變爲 | 回調函數參數值 |
---|---|---|---|
1 | 返回1 個值 | resolved | 返回值 |
2 | 沒有返回 | resolved | undefined |
3 | 拋出錯誤 | rejected | 錯誤 |
4 | resolved的Promise | resolved | 返回的Promise回調的參數值 |
5 | rejected的Promise | rejected | 返回的Promise回調的參數值 |
6 | pending的Promise | pending | 返回的Promise回調的參數值 |
咱們能夠看出想要控制then
的後續流程,必須經過這 6 種狀況來控制 下面來測試一下 這 6 種狀況是否符合咱們的預期
new Promise((resolve, reject) => {
resolve('經過')
}).then((res) => {
console.log('1', res)
return res // then的返回值
}).then((res) => {
console.log('2', res)
})
複製代碼
2. 不返回
3. 拋出錯誤
4. resolved的Promise
5. rejected的Promise
6. pending的Promise
分析特色:
這樣就比較複雜了,還要考慮暫停的問題,可是根據咱們上面測試到的,經過then
的返回值控制流程也沒有想象那麼難
task([101, 201])
function task(testPath) {
console.log('開始測試', testPath)
new Promise((resolve, reject) => {
console.log('000')
if (101 === testPath[0]) {
resolve('101')
} else {
reject('102')
}
}).then((res) => {
console.log('201', '上一個返回:' + res)
return new Promise(() => {})
}, (res) => {
if (202 === testPath[1]) {
console.log('202', '上一個返回:' + res)
return '202'
} else {
console.log('203', '上一個返回:' + res)
throw '203'
}
}).then((res) => {
console.log('301', '上一個返回:' + res)
return Promise.resolve('301')
}, (res) => {
console.log('302', '上一個返回:' + res)
return Promise.resolve('302')
}).then((res) => {
console.log('401', '上一個返回:' + res)
return Promise.reject('401')
}).catch((err) => {
console.log(err)
})
}
複製代碼
Promise.resolve()
等同用 new Promise((resolve, reject) => resolve())
Promise.reject()
等同用 new Promise((resolve, reject) => reject())
Promise.all()
// 做爲參數promise 數組中,全部promise狀態都是resolved纔回調then第一個,只要有一個reject就reject
Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]).then((res) => {
console.log('經過', res)
}, (res) => {
console.log('拒絕', res)
})
複製代碼
返回值爲所有值的一個數組
Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.reject(3)]).then((res) => {
console.log('經過', res)
}, (res) => {
console.log('拒絕', res)
})
複製代碼
返回值僅返回拒絕的那個
Promise.race()
Promise.race([new Promise((resolve, reject) => {
setTimeout(() => {
reject(1)
}, 1001)
}), new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 1002)
})]).then((res) => {
console.log('經過', res)
}).catch((res) => {
console.log('拒絕', res)
})
複製代碼
race 至關於競賽,多個
Promise
競賽,誰先狀態變成
resolved
和
rejected
,誰就執行下面的回調(根據時間來抉擇)
總結一下,從同步異步到回調,在到Promise
的語法和應用所有均可以在谷歌瀏覽器的控制檯中輸出測試。經過一點點代碼的編寫和輸出,纔會讓咱們思惟更清晰,對Promise
的理解更深入。以後再總結工做中用到的Promise
,之後也會慢慢將async
、await
結合Promise
來談關於 es6 之後異步相關的新特性。