本文字數、代碼都比較多,建議先不看代碼,有興趣的時候再仔細研究。javascript
被老學究迫害了十幾年的咱們本能的排斥嚴肅又充滿教育意義的文章。因此老是不想把技術文章寫的那麼嚴肅。今天又是一個被無數人寫過的課題。(此處略過十萬字……)依然略過做者想說的一切廢話,直入主題。java
慢着✋, 讓咱們先來想想,你但願能在這個文章中收穫什麼呢?編程
根據 百度翻譯 介紹,promise
主要有下面幾種含義:數組
咳咳……不知不覺就開錯了車,籲~及時的剎住。promise
JavaScript
中的 promise
是用於處理 異步
問題的 工具
。沒錯,它就是一個 工具
。跟你作飯用的炒勺和鍋鏟是同一個定義。緩存
它能夠將繁瑣的異步問題經過更加易讀方式來處理,從而提升代碼的可閱讀性,減小維護所帶來的成本。異步
小栗子走一波:async
function change1(data) {
// 處理data……
change2(data)
function change2(data) {
// 處理data……
change3(data)
function change3(data) {
// 處理data……
change4(data)
function change4(data) {
// 處理data……
change5(data)
function change5(data) {
// 處理data……
change6(data)
function change6(data) {
………………
}
}
}
}
}
}
複製代碼
Promise.resolve(data)
.then(data => data) // 處理data
.then(data => data) // 處理data
.then(data => data) // 處理data
.then(data => data) // 處理data
.then(data => data) // 處理data
.then(data => data) // 處理data
.then(data => data) // 處理data
………………
複製代碼
先拋開其餘全部的都不說,這一排整整齊齊的代碼隊伍,看上去就賞心悅目。異步編程
接下來,咱們能夠更進一步了。函數
剛纔咱們提到了,promise
是用來處理 異步
問題的工具。什麼是 異步
? 不是 異步
的問題又是什麼?小朋友,你是否有不少問號 ?????
任務分爲 異步
和 同步
,不說官方的定義了,經過一段講解來講吧。
**同步:**列舉一個場景,假設你如今在銀行辦理業務,若是你前面的那我的辦理不完,確定是不能給你辦理的。同一時間,業務員只能處理一個業務,當上一個業務到達完成狀態(也就是處理完)的時候,才能接受下一個業務。
**異步:**一樣,列舉一個場景,場景變換到飯店,接待你的是一位服務員,服務員點完菜以後就會將菜單交到廚師手裏,而後就能夠繼續接待下一位顧客。並不須要等到當前顧客的服務結束。不須要等待複雜的、消耗時間的炒菜操做的結束。
多麼簡單的道理,茅塞頓開的感受有木有……
裝13的話就很少說了,正經起來。
要解釋這個問題,得先弄懂什麼是回調地獄
的問題,回調地獄出現的緣由是由於異步和回調函數。以吃披薩這件事爲慄來講,想吃披薩必須得經歷如下幾步:
想吃披薩 --> 得有披薩店 --> 店裏得有廚師 --> 廚師得有食材 --> 菜市場得出售食材 --> 收穫製做食材的原材料
其中任何一步有問題,我都不可能吃到披薩。(這裏拒絕擡槓。怕了怕了)
以代碼的形式出現:
function yuancailiao() {
// 收穫原材料
function chushou() {
// 出售食材
function shicai() {
// 食材到披薩店裏
function chushi() {
// 食材到廚師手中
function dameile() {
// 披薩店製做披薩
function chi () {
// 歷經千辛萬苦,我終於吃到了披薩。
}
}
}
}
}
}
複製代碼
當嵌套的層級太深,就會在查找代碼的時候出現暈眩的感受,讓你出現暈眩的代碼就成爲回調地獄。
Promise.resolve('披薩店')
.then(data => data) // 購買原材料
.then(data => data) // 製做披薩
.then(data => data) // 上桌
.then(data => data) // 開始大快朵eat
………………
複製代碼
哇哦~空氣都變得清新了。(彷彿一樣的代碼我寫了兩次~~~但願能不被打)
這也就是promise
出現的緣由。
promise
在運行的過程當中會出現三種狀態。
狀態 | 描述 |
---|---|
pending | 初始狀態 表示待定 |
fulfilled | 操做成功 |
rejected | 操做失敗 |
狀態只能從 pending
到fulfilled
或者 rejected
。不可逆。
圖解:
終於到了手擼代碼的環節。準備好了嗎?come on baby……
寫代碼以前咱們先看下promise
都有哪些須要實現的功能
可以使用new
實例化
接收一個函數做爲參數
函數中有 resolve
和 reject
方法
三種狀態
可以使用 .then
和 .catch
操做
可拋出錯誤或執行錯誤均可經過 .catch
進行捕獲
鏈式調用
好像沒有其餘的了,先寫。
// 先定義一個Promise構造函數
function Promise (executor) {
// 定義一個狀態,初始值爲pending
this.status = 'pending'
// 成功方法
this.success = () => {}
// 失敗方法
this.error = () => {}
// 定義resolve函數
function resolve(data) {
if (this.status === 'pending') {
this.status = 'fulfilled'
// 成功後執行成功方法,並將獲取的數據過去
this.success(data)
}
}
// 定義reject函數
function reject (errorMsg) {
if (this.status === 'pending') {
this.status = 'rejected'
this.error(errorMsg)
}
}
// 將 resolve 和 reject 傳入到入參函數中,並 ---> 綁定this !!!
executor(resolve.bind(this), reject.bind(this))
}
複製代碼
// 定義 then 方法,接收兩個函數做爲參數 success = () => {}, error = () => {}
Promise.prototype.then = function (success, error) {
this.success = success
this.error = error
}
// 定義 catch 方法
Promise.prototype.catch = function (error) {
this.error = error
}
複製代碼
好,一個嶄新的Promise
就完成了。自信一試。
new Promise1((res, rej) => {
res(1)
}).then((data) => {
console.log(data)
})
// 什麼也沒輸出。
複製代碼
納尼。!跟個人預想徹底不相符啊。機智如我怎麼可能被一個小bug絆住了雙腳。一陣緊鑼密鼓的調試,終於讓我發現了問題的所在。在resolve
方法執行的時候,this.success
還未被賦值。改爲下面這樣就能夠正常輸出了。
new Promise1((res, rej) => {
setTimeout(() => {
res(1)
}, 0)
}).then((data) => {
console.log(data)
})
複製代碼
// 先定義一個Promise1構造函數
function Promise1 (executor) {
// 定義一個狀態,初始值爲pending
this.status = 'pending'
// 成功方法
this.successList = []
//
// 失敗方法
this.errorList = []
// 添加 value緩存 --- 新添加 ---
this.value = null
// 定義resolve函數
function resolve(data) {
if (this.status === 'pending') {
this.status = 'fulfilled'
// 成功後執行成功方法,並將獲取的數據過去
// --- 新添加 ---
this.value = data
this.successList.forEach(cb => cb(data))
}
}
// 定義reject函數
function reject (errorMsg) {
if (this.status === 'pending') {
this.status = 'rejected'
// --- 新添加 ---
this.value = errorMsg
this.errorList.forEach(cb => cb(errorMsg))
}
}
// 將 resolve 和 reject 傳入到入參函數中,並 ---> 綁定this !!!
executor(resolve.bind(this), reject.bind(this))
}
// 定義 then 方法,接收兩個函數做爲參數 success = () => {}, error = () => {}
Promise1.prototype.then = function (success, error) {
// 若是執行時狀態已經更改,直接拿取緩存的值
if (this.status === 'fulfilled') {
success(this.value)
}
if (this.status === 'rejected') {
error(this.value)
}
// 不然將當前函數保存
if (this.status === 'pending') {
this.successList.push(success)
this.errorList.push(error)
}
}
// 定義 catch 方法
Promise1.prototype.catch = function (error) {
if (this.status === 'pending') {
this.error = error
return
}
error(this.value)
}
new Promise1((res, rej) => {
setTimeout(() => {
res(1)
}, 0)
}).then((data) => {
console.log(data)
})
複製代碼
原諒個人罪過,讓你們一次看這麼多代碼,實屬是個人問題。之後儘可能改正。
promise
接受的參數能夠顯示的拋出錯誤,因此咱們須要將錯誤捕獲。很簡,捕獲到executor
的錯誤就能夠
try {
// 將 resolve 和 reject 傳入到入參函數中,並 ---> 綁定this !!!
executor(resolve.bind(this), reject.bind(this))
} catch (e) {
reject(e)
}
複製代碼
鏈式調用實現的核心是須要在 then
方法中返回一個新的promise
。這裏有一個須要注意的點。resolve
的數據要使用resolve
函數包裹,reject
的數據要使用reject
函數包裹。
Promise1.prototype.then = function (success, error) {
// 若是執行時狀態已經更改,直接拿取緩存的值
if (this.status === 'fulfilled') {
return new Promise1((resolve, reject) => {
try{
resolve(success(this.value))
}catch(e) {
reject(e)
}
})
}
if (this.status === 'rejected') {
return new Promise1((resolve, reject) => {
try{
resolve(error(this.value))
}catch(e) {
reject(e)
}
})
}
// 不然將當前函數保存
if (this.status === 'pending') {
return new Promise1((resolve, reject) => {
this.successList.push(() => {
try {
resolve(success(this.value))
} catch (e) {
reject(e)
}
})
this.errorList.push(() => {
try {
resolve(error(this.value))
} catch (e) {
reject(e)
}
})
})
}
}
複製代碼
測試一下:
new Promise1((res, rej) => {
setTimeout(() => {
res(1)
}, 0)
}).then((data) => {
console.log(data)
}).then(() => {
console.log(2)
}).then(() => {
console.log(3)
})
複製代碼
ok,大功告成。
promise.all
方法的特色
promise
對象栗子實現:
Promise.newAll = arr => {
let result = [];
return new Promise(function (resolve, reject) {
let count = 0;
arr.forEach(item => {
item.then((res) => { // arr中爲promise的列表,因此直接執行then方法。
result.push(res)
count ++
// 若是所有成功,經過resolve返回獲得的結果
if (count === arr.length) {
resolve(result)
}
}).catch((e) => {
// 只要有一個報錯,就執行reject
reject(e)
})
})
})
};
複製代碼
promise.race
方法的特色
promise
對象栗子實現:
Promise.newRace = arr => {
return new Promise(function (resolve, reject) {
let count = 0;
arr.forEach(item => {
item.then((res) => { // arr中爲promise的列表,因此直接執行then方法。
// 返回最快獲得的內容
resolve(res)
}).catch((e) => {
count ++
// 若是所有失敗,經過resolve返回獲得的結果
if (count === arr.length) {
reject(e)
}
})
})
})
};
複製代碼
不知不覺又寫成了老學究,仍是太年輕。
如今介紹一下上面這兩個傢伙吧。
async 和 await
是JavaScript
提供的異步編程的語法糖(能甜死你的那種)。
先看小栗子
function see(){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve(1)
},3000)
})
}
async function test(){
let result = await see()
console.log(result)
}
test()
複製代碼
async
會將一個函數標記爲異步函數,經過await
等待結果的返回,而且他們還會改變事件循環 (文章敬請期待) 的執行順序。而且。它們還可使用 try {} catch() {}
捕獲錯誤。
又是嘔心瀝血寫的一篇文章。看完文章的你。理解了什麼呢?