說到ES6的Promise,大家并不陌生,它是JS中解決異步的方法之一
其優點:避免回呼地獄,鏈式呼叫,函式思路清晰,邏輯相對回呼函式和事件發布/訂閱更強
缺點:理解性差,異步操作在promise建構式內部
這段時間在整理面試題時發現,Promise的實作可以單獨拿出來分享,于是自己簡單實作了一下
碼云地址:https://gitee.com/DieHunter/myCode/tree/master/Promise%E5%B0%81%E8%A3%85
實作完整功能之前,我們先了解一下Promise的用法,并實作一個簡單的Promise和Promise.then函式
Promise(executor: (resolve: (value?: any) => void, reject: (reason?: any) => void) => void): Promise<any>
上述配置提示中顯示,Promise需要傳入一個回呼函式,函式有兩個引數(resolve, reject),第一個是異步執行成功后回呼,另一個是失敗時的回呼,Promise.then方法是執行異步函式成功,即resolve執行時,才會執行then方法中的回呼,以下是Promise最簡單的一個用法
new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('success')// 傳參
}, 500)
}).then(function (res) {
console.log(res) // success
})
下面,我們實作一個最簡單的Promise,用于決議Promise.then的原理,主要原理就是用兩個回呼函式嵌套,將函式作為引數放入至異步操作中,當異步操作執行后再執行作為引數的回呼
function MyPromise(fn) { // 主要原理就是用兩個回呼函式嵌套,將函式作為引數放入至異步操作中,當異步操作執行后再執行作為引數的回呼
var _this = this;
_this.params = null; // 傳遞的引數
_this.tempResolve = null // _this.tempResolve的作用是將引數傳遞至then方法中
function resolve(params) { // 異步操作之后才會執行該方法,執行前一直等待
_this.params = params
_this.tempResolve(_this.params)
}
fn(resolve) // 將resolve通過回呼回傳到異步操作函式中,當resolve執行時,才是異步操作執行后
}
MyPromise.prototype.then = function (_resolve) { // 異步操作傳遞引數,簡言之就是連接then和resolve
var _this = this
_this.tempResolve = function () {
_resolve(_this.params)
}
}
MyPromise.prototype.constructor = MyPromise
new MyPromise(function (res, rej) {
setTimeout(function () {
res('success')
}, 1000)
})
.then(function (res) {
console.log(res) // success
})
如果理解了上面的代碼,就已經成功了一半,接下來,我們對Promise進行深入的實作,與上述代碼差別是,添加then的鏈式呼叫,其實可以理解為多層Promise嵌套,但是我們需要對每層Promise做出操作,所以,我們在每層promise中添加status用于記錄當前promise是否已執行,tempResolve也要改成tempResolveList,因為需要執行的函式不止一個,變成了一個佇列,在上面代碼的基礎上,我們對resolve進行優化
function resolve(params) { // 異步操作之后才會執行該方法,執行前一直等待
if (_this.status === 'pending') {
_this.status = 'resolve'; // 進入函式后,立即修改函式狀態,防止下面的回圈重復執行函式
_this.params = params;
for (var i = 0; i < array.length; i++) {
_this.tempResolveList[i](_this.params) // 執行所有then的鏈式呼叫的函式
}
}
}
除此之外,在then函式中,還需要添加一段代碼,其目的是將Promise回傳到下一層鏈式呼叫,將回呼函式通過resolve傳遞至下一層,達到依次同步執行的目的
MyPromise.prototype.then = function (tempResolve) { // 異步操作傳遞引數,簡言之就是連接then和resolve
var _this = this
var _promise = new MyPromise(function (resolve, reject) {
if (_this.status == 'pending') {
_this.tempResolveList.push(function () {
resolve(tempResolve(_this
.params)) // 將上一層tempResolve通過resolve的引數異步傳遞給下一層的Promise中,每層都會異步疊加
})
}
})
return _promise // 回傳Promise用于鏈式呼叫
}
完成之后,我們會發現一個問題,當我們通過resolve傳遞tempResolve執行結果時,只有一層鏈式呼叫的話,回傳的是原回呼函式,當到了第二層時,回傳的是上一層的resolve,此時我們需要在resolve函式之前做個過濾,并且把引數中的then放在本層,直接執行
if (params && typeof params === 'function' || typeof params ===
'object') { // 這里要判斷引數是普通引數params,還是MyPromise方法,鏈式呼叫一定會產生MyPromise建構式
var _then = params.then // 如果引數是MyPromise建構式,則將上層的then放到本層繼續執行后續操作
if (typeof _then === 'function') {
_then.call(params, resolve); // 鏈式呼叫then
return;
}
}
Promise.then的鏈式呼叫完整代碼
function MyPromise(fn) { // 主要原理就是用兩個回呼函式嵌套,將函式作為引數放入至異步操作中,當異步操作執行后再執行作為引數的回呼
var _this = this;
_this.status = 'pending'; // 每層Promise的待定狀態,只有當前Promise處于pending的時候,才會執行異步函式
_this.params = null; // 傳遞的引數
_this.tempResolveList = new Array() // 儲存鏈式呼叫then中的函式佇列
function resolve(params) { // 異步操作之后才會執行該方法,執行前一直等待
if (params && typeof params === 'function' || typeof params ===
'object') { // 這里要判斷引數是普通引數params,還是MyPromise方法,鏈式呼叫一定會產生MyPromise建構式
var _then = params.then // 如果引數是MyPromise建構式,則將上層的then放到本層繼續執行后續操作
if (typeof _then === 'function') {
_then.call(params, resolve); // 鏈式呼叫then
return;
}
}
if (_this.status === 'pending') {
_this.status = 'resolve'; // 進入函式后,立即修改函式狀態,防止下面的回圈重復執行函式
_this.params = params;
for (var i = 0; i < _this.tempResolveList.length; i++) {
_this.tempResolveList[i](_this.params) // 執行所有then的鏈式呼叫的函式
}
}
}
fn(resolve) // 將resolve通過回呼回傳到異步操作函式中,當resolve執行時,才是異步操作執行后
}
MyPromise.prototype.then = function (tempResolve) { // 異步操作傳遞引數,簡言之就是連接then和resolve
var _this = this
var _promise = new MyPromise(function (resolve, reject) {
if (_this.status == 'pending') {
_this.tempResolveList.push(function () {
resolve(tempResolve(_this
.params)) // 將上一層tempResolve通過引數異步傳遞給下一層的Promise中,每層都會異步疊加
})
}
})
return _promise // 回傳Promise用于鏈式呼叫
}
MyPromise.prototype.constructor = MyPromise
var count = 1
new MyPromise(function (res, rej) {
setTimeout(function () {
res('success' + count++)
}, 1000)
})
.then(function (res) {
console.log(res) // success1
return new MyPromise(function (res, rej) {
setTimeout(function () {
res('success' + count++)
}, 1000)
})
}).then(function (res) {
console.log(res) // success2
return new MyPromise(function (res, rej) {
setTimeout(function () {
res('success' + count++)
}, 1000)
})
}).then(function (res) {
console.log(res) // success3
})
實作了鏈式呼叫后,我們對reject以及catch進行一個簡單的實作,其實作程序與then相似,我們對一些方法封裝一下,得到以下代碼(catch沒有完善鏈式呼叫,導致then方法執行數量大于1時失效)
function MyPromise(fn) { // 主要原理就是用兩個回呼函式嵌套,將函式作為引數放入至異步操作中,當異步操作執行后再執行作為引數的回呼
var _this = this;
_this.status = 'pending'; // 每層Promise的待定狀態,只有當前Promise處于pending的時候,才會執行異步函式
_this.params = null; // 傳遞的引數
_this.tempResolveList = new Array() // 儲存鏈式呼叫then中的函式佇列
_this.tempRejectList = new Array() // 儲存鏈式呼叫catch中的函式佇列
_this.runCommandList = function (_status, _params,
_commandList) { // 若函式狀態是pending待定狀態,函式執行后會有兩個狀態,resolve和reject
if (_params && typeof _params === 'function' || typeof _params ===
'object') { // 這里要判斷引數是普通引數params,還是MyPromise方法,鏈式呼叫一定會產生MyPromise建構式
var _then = _params.then // 如果引數是MyPromise建構式,則將上層的then放到本層繼續執行后續操作
if (typeof _then === 'function') {
_then.call(_params, resolve); // 鏈式呼叫then
return;
}
}
if (_this.status === 'pending') {
_this.status = _status; // 進入函式后,立即修改函式狀態,防止下面的回圈重復執行函式
_this.params = _params;
for (var i = 0; i < _commandList.length; i++) {
_commandList[i](_this.params) // 執行所有then的鏈式呼叫的函式
}
}
}
_this.runCallBack = function (resolve, reject, finishFn) {
return function () {
try {
var temp = finishFn(_this.params);
resolve(temp);
} catch (error) {
reject(error);
}
}
}
_this.createPromise = function (temp, tempList) {
var _this = this
return new MyPromise(function (resolve, reject) {
if (_this.status == 'pending') {
tempList.push(_this.runCallBack(resolve, reject,
temp)) // 將上一層tempResolve通過引數異步傳遞給下一層的Promise中,每層都會異步疊加
}
})
}
function resolve(params) { // 異步操作之后才會執行該方法,執行前一直等待,通過回呼回傳到new Promise(fn)引數中
_this.runCommandList('resolve', params, _this.tempResolveList)
}
function reject(params) { // 異步操作之后才會執行該方法,執行前一直等待,通過回呼回傳到new Promise(fn)引數中
_this.runCommandList('reject', params, _this.tempRejectList)
}
try { //捕獲例外
fn(resolve, reject)
} catch (error) {
reject(error)
} // 將resolve通過回呼回傳到異步操作函式中,當resolve執行時,才是異步操作執行后
}
MyPromise.prototype.then = function (tempResolve) { // 異步操作傳遞引數,簡言之就是連接then和resolve
var _this = this
var _promise = _this.createPromise(tempResolve, _this.tempResolveList)
_promise.catch = function (tempReject) { // 異步操作傳遞引數,簡言之就是連接then和resolve
_this.createPromise(tempReject, _this.tempRejectList)
}
return _promise // 回傳Promise用于鏈式呼叫
}
MyPromise.prototype.constructor = MyPromise
var count = 1
new MyPromise(function (res, rej) {
setTimeout(function () {
rej('success' + count++)
}, 1000)
// setTimeout(function () {
// res('success' + count++)
// }, 1000)
})
.then(function (res) {
console.log(res) // success1
return new MyPromise(function (res, rej) {
setTimeout(function () {
res('success' + count++)
}, 1000)
})
}).catch(function (err) {
console.log(err) // success1
})
總結:代碼可能有地方不完善,歡迎大佬指出
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/224816.html
標籤:其他
上一篇:正則運算式使用筆記
下一篇:學習Typora這一篇就夠了
