一、Promise的使用
在學習如何寫Promise之前,先要熟悉Promise的使用,然後一步一步去實作
【Promise的基本使用】:
1. Promise 是一個類 在執行這個類的時候需要傳遞一個執行器進去,執行器會立即執行
2. Promise中有三種狀态,分别是:等待pending 成功fulfilled 失敗rejected(狀态一旦确定不可改變)
3. 執行器接收兩個參數resolve(成功執行的方法)和reject(失敗執行的方法)
4. Promise擁有then方法,方法内部需判斷狀态,接收了兩個回調函數,如果成功調用成功的回調,如果失敗調用失敗的回調。then方法是被定義在原型對象中的
const promise = new Promise((resolve, reject) => {
resolve('成功')
// reject('失敗')
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
二、手寫Promise
【實作步驟】:
- 建立MyPromise類
- 通過構造函數constructor,在執行這個類的時候需要傳遞一個執行器進去并立即調用
- 定義resolve和reject(定義為箭頭函數:避免直接調用時this指向全局window問題)
- 定義狀态常量(成功fulfilled 失敗rejected 等待pending),初始化為pending。
- 完成resolve和reject函數的狀态改變(注意:需判斷目前狀态是否可以改變)
- MyPromise類中定義value和reason,用來儲存執行器執行成功和失敗的傳回值
- MyPromise類中添加then方法,成功回調有一個參數 表示成功之後的值;失敗回調有一個參數 表示失敗後的原因
- 處理異步邏輯(pending狀态下在then中将回調存起來)
- 實作then方法多次調用添加多個處理函數
- 實作then方法鍊式調用(寫一個函數方法專門判斷回調的結果是普通值還是promise,then方法傳回的仍然是一個promise)
- 處理promise傳回值各種類型情況(普通值,promise)
- then方法鍊式調用識别Promise對象自傳回
- Promise實作捕獲錯誤及then鍊式調用其他狀态代碼補充
- 将then方法的參數變為可選參數
- Promise.all
- Promise.resolve 傳回一個promise
- finally方法 不管成功失敗都會執行一次
- catch方法的實作
【代碼】:
// 4. 定義狀态常量(成功fulfilled 失敗rejected 等待pending),初始化為pending。
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// 1 建立MyPromise類
class MyPromise {
// 2 通過構造函數constructor,在執行這個類的時候需要傳遞一個執行器進去并立即調用
constructor(executor) {
// 13 Promise實作捕獲錯誤
try {
executor(this.resolve, this.reject)
} catch (e) {
this.reject(e)
}
}
status = PENDING
// 6. MyPromise類中定義value和reason,用來儲存執行器執行成功和失敗的傳回值
value = null
reason = null
// 9. 實作then方法多次調用添加多個處理函數 初始化回調為數組依次執行
successCallback = []
failCallback = []
// 3. 定義resolve和reject(定義為箭頭函數:避免直接調用時this指向全局window問題)
resolve = value => {
// 5. 完成resolve函數的狀态改變(注意:需判斷目前狀态是否可以改變)
// 判斷目前狀态是否可改變
if(this.status !== PENDING) return
// 改變目前狀态
this.status = FULFILLED
// 儲存傳回值
this.value = value
// 執行成功回調
while(this.successCallback.length) {
this.successCallback.shift()(this.value)
}
}
reject = reason => {
// 5. 完成reject函數的狀态改變(注意:需判斷目前狀态是否可以改變)
// 判斷目前狀态是否可改變
if(this.status !== PENDING) return
// 改變目前狀态
this.status = REJECTED
// 儲存傳回值
this.reason = reason
// 執行失敗回調
while(this.failCallback.length) {
this.failCallback.shift()(this.reason)
}
}
// 7. MyPromise類中添加then方法,成功回調有一個參數 表示成功之後的值;失敗回調有一個參數 表示失敗後的原因
then(successCallback, failCallback) {
// 14 将then方法的參數變為可選參數
successCallback = successCallback ? successCallback : value => this.value
failCallback = failCallback ? failCallback : reason => {throw this.reason}
// 10. 實作then方法鍊式調用(寫一個函數方法專門判斷回調的結果是普通值還是promise,then方法傳回的仍然是一個promise)
let promise2 = new MyPromise((resolve, reject) => {
// 判斷目前狀态 執行對應回調 異步情況下存儲目前回調等待執行
if(this.status === FULFILLED) {
// 異步
setTimeout(() => {
// 13 then方法捕獲錯誤
try {
// 異步擷取到promise2
let x = successCallback(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
} else if(this.status === REJECTED) {
// 異步
setTimeout(() => {
// 13 then方法捕獲錯誤
try {
// 異步擷取到promise2
let x = failCallback(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
} else {
// 8. 處理異步邏輯(pending狀态下在then中将回調存起來)
this.successCallback.push(() => {
try {
let x = successCallback(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
this.failCallback.push(() => {
try {
let x = failCallback(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
}
})
return promise2
}
// 17. finally方法 不管成功失敗都會執行一次
finally(callback) {
return this.then(value => {
return MyPromise.resolve(callback()).then(() => value)
}, reason => {
return MyPromise.reject(callback()).then(() => { throw reason })
})
}
// 18. catch
catch(failCallback) {
return this.then(undefined, failCallback)
}
// 15. Promise.all
static all (array) {
let result = []
let index
return new Promise((resolve, reject) => {
function addData(key, value) {
result[key] = value
index++
if(index === array.length) {
resolve(result)
}
}
for(let i = 0; i < array.length; i++) {
let current = array[i]
if(current instanceof MyPromise) {
current.then(value => addData(i, value), reason => reject(reason))
} else {
addData(i, array[i])
}
}
})
}
// 16. Promise.resolve 傳回一個promise
static resolve(value) {
if(value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
}
// 處理promise傳回值各種類型情況(普通值,promise)
function resolvePromise(promise2, x, resolve, reject) {
if(promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if(x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
【對應驗證代碼】:
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./promise2.js"></script>
<!-- <script src="./newPromise.js"></script> -->
<script>
// 1、基礎
let promise = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失敗')
})
promise.then(value=>{
console.log(value)
},reason=>{
console.log(reason)
})
// 2、異步
let promise = new Promise((resolve, reject) => {
setTimeout(() => { // 異步
resolve('成功')
}, 2000)
// reject('失敗')
})
promise.then(value=>{
console.log(value)
},reason=>{
console.log(reason)
})
// 3、then方法鍊式調用
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => { // 異步
resolve('成功')
}, 2000)
// reject('失敗')
})
function other () {
return new MyPromise((resolve, reject) => {
resolve('other')
})
}
promise.then(value=>{
console.log(value)
return other()
}).then(value => {
console.log(value)
})
// 4、promise對象子傳回循環報錯
var promise = new Promise(function(resolve, reject) {
resolve(100)
})
var p1 = promise.then(function(value) {
return p1
})
p1.then(value => {
console.log(value)
}, reason => {
console.log(reason.message)
})
// 5、Promise實作捕獲錯誤及then鍊式調用其他狀态代碼補充
var promise = new MyPromise(function(resolve, reject) {
// setTimeout(() => {
// resolve('成功。。。')
// },2000)
throw new Error('executor error')
// resolve('成功')
})
promise.then(value => {
console.log(value)
// throw new Error('then error')
return 'aaa'
}, reason => {
console.log('報錯')
console.log(reason)
return '123'
}).then(value => {
console.log('value2')
console.log(value)
}, reason => {
console.log('報錯2')
console.log(reason)
})
// 6、将then方法的參數變為可選參數
var promise = new MyPromise(function(resolve, reject) {
resolve(100)
})
promise.then().then(value => value).then(value => console.log(value))
// 7、Promise.all
// 按照異步代碼調用順序得到結果
// 類直接調用的方法是靜态方法
function p1() {
return new Promise(function (resolve, reject) {
setTimeout(function() {
resolve('p1')
},2000)
})
}
function p2() {
return new Promise(function (resolve, reject) {
resolve('p2')
})
}
Promise.all(['a', 'b', p1(), p2(), 'c']).then(function(result) {
// result -> ['a', 'b', 'p1', 'p2', 'c']
console.log(result)
})
// 8、Promise.resolve将給定的值轉換為promise對象
function p1() {
return new Promise(function (resolve, reject) {
resolve('hello')
})
}
Promise.resolve(10).then(value => console.log(value))
Promise.resolve(p1()).then(value => console.log(value))
// 9、finally方法無論promise執行成功或失敗finally都會執行一次
// finally方法後可鍊式調用then方法拿到最終傳回的結果
function p1() {
return new Promise(function (resolve, reject) {
reject('hello')
})
}
p1().finally(() => {
console.log('finally')
}).then(value => {
console.log(value)
}, reason => {
console.log('error')
console.log(reason)
})
// 10、catch
function p1() {
return new Promise(function (resolve, reject) {
resolve('hello')
})
}
p1()
.then(value => {
return a
})
.then(value => console.log('value2:' + value))
.catch(reason => console.log('catch:' + reason))
</script>
</body>
</html>