function oPromise(fn) {
var value = null,
callbacks = [] //callbacks爲數組, 由於可能同時有不少個回調
this.then = function (fulfilled) {
callbacks.push(fulfilled)
return this //
}
function resolve(newValue) {
value = newValue
callbacks.forEach(function (callback) {
callback(value)
})
}
fn(resolve)
}
new oPromise((resolve) => { //省略了rejected, 方便之後閱讀, 由於與resolve原理同樣
setTimeout(() => { //處理異步
resolve('dz')
}, 0)
}).then(res => {
console.log(res)
})
複製代碼
大體邏輯:javascript
fn()
, 傳入的是內部resolve
函數, 由於傳入的回調有setTimeout
定時器是一個異步, 因此resolve
會被延遲執行then
方法, 把then
包含的回調註冊(添加)到callbacks
數組中, 由於返回了實例->this
, 所以若是有序還有其餘then
會一次被註冊到callbacks
中setTimeout
, 並執行了resolve
, 循環執行數組, 而後把value
賦值給回調問題:java
setTimeout
, 可是若是沒有設置的話, oPromise內部的回調執行任然是同步, 這樣會致使then
方法尚未執行就執行了resolve
, 此時callbacks裏仍是空的..意思就是說, 實例化的時候傳入了執行函數, 這個函數內部並非全部時候都是異步的, 也有時候是同步代碼例如:數組
new oPromise(resolve => {
let a = 1 + 1
resolve(a)
}).then(res => console.log) // 2
複製代碼
這個時候全部代碼都是同步的所以會先執行回調, 而後再執行 then, 這樣的話 callbacks 數組先被執行, 而後最後添加回調, 這樣的意義何在呢...promise
所以, 將 resolve 延遲執行(也知足了 Promise/A+ 規範):bash
// resolve 函數
function resolve(newValue) {
value = newValue
setTimeout(() => {
callbacks.forEach(function (callback) {
callback(value)
})
}, 0)
}
複製代碼
Promises/A+規範規定, pending能夠轉化爲fulfilled或rejected而且只能轉化一次, 也就是說若是pending轉化到fulfilled狀態, 那麼就不能再轉化到rejected. 而且fulfilled和rejected狀態只能由pending轉化而來, 二者之間不能互相轉換
複製代碼
加上狀態後:異步
function oPromise(fn) {
let value = null,
state = 'pending'
callbacks = [] //callbacks爲數組, 由於可能同時有不少個回調
this.then = function (fulfilled) {
if (state === 'pending') {
callbacks.push(fulfilled)
} else {
fulfilled(value)
}
return this //返回實例
}
function resolve(newValue) {
if (state !== 'pending') return
setTimeout(() => {
value = newValue
state = 'fulfilled'
callbacks.forEach(function (callback) {
callback(value)
})
}, 0)
}
fn(resolve)
}
new oPromise((resolve) => { //省略了rejected, 方便之後閱讀, 由於與resolve原理同樣, 後續再加入
setTimeout(() => { //處理異步
resolve('dz')
}, 0)
}).then(res => {
console.log(res)
return 'dz'
})
複製代碼
resolve執行時將狀態變爲fulfilled
, 若是回調是異步, 就會先執行 then, 此時的 state 爲pending
, 就將 then 註冊的回調添加到callbacks中, 若是回調不是異步, 就會由於state爲 fulfilled
而當即執行函數
在 resolve
方法中第一條語句是判斷狀態是否爲 pending
, 這條語句的緣由是: 一旦狀態被 resolve 或者 reject 改變後 就不能再改變了, 在後續 寫到 加入 reject
後 再詳細說明post
每一次調用 fulfilled
回調 咱們都把返回值保存下來, 以讓下一個then中的回調(也就是下一個 fulfilled)得到返回值, 簡單改造一下ui
function oPromise(fn) {
let value = null,
state = 'pending'
callbacks = [] //callbacks爲數組, 由於可能同時有不少個回調
this.then = function (fulfilled) {
if (state === 'pending') {
callbacks.push(fulfilled)
} else {
fulfilled(value)
}
return this //返回實例
}
function resolve(newValue) {
if (state !== 'pending') return
setTimeout(() => {
value = newValue // 這裏放在setTimeout才比較正確 否則不能鏈式調用
state = 'fulfilled'
callbacks.forEach(function (callback) {
value = callback(value) // 保存上一個value: 關鍵
})
}, 0)
}
fn(resolve)
}
new oPromise((resolve) => { //省略了rejected, 方便之後閱讀, 由於與resolve原理同樣
setTimeout(() => { //處理異步
resolve('dz')
}, 0)
}).then(res => {
console.log(res)
return 'dz'
}).then(res => {
console.log(res)
})
複製代碼
雖然上面看似沒有問題, 但其實有很大的缺陷:this
若是then的執行回調
中的代碼爲異步, 此時不能保證異步執行後 返回的值能被下一個then執行回調所接收. 不對, 是確定不能
所以, 解決異步並回調的根本方式就是回到最初的問題 --> Promise
若是then回調函數中的代碼有異步代碼, 就將異步代碼放置在 Promise
中, 而後將返回值傳給下一個 then
中的回調, 這樣便造成 層層鏈式
, 多安逸...
鏈式Promise是指在當前promise達到fulfilled狀態後, 即開始進行下一個promise(後鄰promise)
所以就須要在then方法裏return一個Promise
function oPromise(fn) {
let value = null,
state = 'pending',
callbacks = [] //callbacks爲數組, 由於可能同時有不少個回調
this.then = function (fulfilled) {
return new oPromise((resolve) => {
handle({
fulfilled: fulfilled || null,
resolve // 將下一個promise的resolve和then的回調一塊兒添加到callbacks中, 爲了在本次執行resolve時, 將本次的返回值傳遞到下一個promise
})
})
}
function handle(callback) {
if (state === 'pending') {
callback.push(callback)
return
}
if (!callback.fulfilled) { // then 沒有回調函數
callback.resolve(value) // 就直接將value值傳給-> 下 -> 下 個promise(若是有的話)
return
}
const ret = callback.fulfilled(value) // 接受上一個promise返回值
callback.resolve(ret) // 傳入下一個promise... 有點暈, 要仔細想一想..
}
function resolve(newValue) { //若是then函數的回調爲一個新的promise, 須要作一些特殊處理
if (state !== 'pending') return
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
const then = newValue.then // 獲取 promise的then
if (typeof then === 'function') {
then.call(newValue, resolve) // 添加then方法的回調, 把本次then回調的執行結果傳給下一個promise, 並執行
return
}
}
const fn = () => {
value = newValue
state = 'fulfilled'
handleCb()
}
setTimeout(fn, 0)
}
function handleCb() {
while (callbacks.length) {
const cb = callbacks.shift()
handle(cb)
}
}
fn(resolve)
}
複製代碼
整個過程的關鍵在於handle
函數的執行, 它分擔了聯繫相鄰兩個promise的做用:
添加
回調仍是執行
, 若是沒有回調就跳過本次promise執行再下一個then(promise)pending
就是添加回調, 把下一個then產生promise中的resolve
一塊兒添加fulfilled
就是執行, 就會調用下一個promise的resolve, 並傳入本次回調產生的結果值在異步操做失敗時, 標記其狀態爲rejected, 並執行註冊的失敗回調
複製代碼
function oPromise(fn) {
let value = null,
state = 'pending',
callbacks = [] //callbacks爲數組, 由於可能同時有不少個回調
this.then = function (fulfilled, rejected) {
return new oPromise((resolve, reject) => {
handle({
fulfilled: fulfilled || null,
rejected: rejected || null,
resolve,
reject // 和resolve同樣, 將一下個promise的錯誤執行(reject)加入callbacks中
})
})
}
function handle(callback) {
if (state === 'pending') {
callbacks.push(callback)
return
}
let cb = state === 'fulfilled' ? callback.fulfilled : callback.rejected
let r = state === 'fulfilled' ? callback.resolve : callback.reject
let ret = null
if (cb === null) {
cb(value)
return
}
ret = cb(value)
r(ret)
}
// 省略 resolve, 減小篇幅. function resolve(newValue) {...}
function reject(reason) {
if (state !== 'pending') return
const fn = () => {
if (state !== 'pending') return
if (reason && (typeof reason === 'object' || typeof reason === 'function')) {
const then = reason.then // 獲取 promise的then
if (typeof then === 'function') {
then.call(reason, resolve) // 添加then方法的回調, 把本次then回調的執行結果傳給下一個promise, 並執行
return
}
}
state = 'rejected'
value = reason
handleCb()
}
setTimeout(fn, 0)
}
function handleCb() { // 將公共部分處理提取出來
while (callbacks.length) {
const cb = callbacks.shift()
handle(cb)
}
}
fn(resolve, reject)
}
複製代碼
加入了失敗狀態的處理以後, 大體原理仍是同樣, 只是在執行callbacks裏面的回調和執行下一個promise的處理(resolve或者reject)須要作一些判斷
這裏說一下 resolve
和 reject
方法中第一個判斷語句的做用, 舉個栗子, 假如沒有判斷:
const promise = new oPromise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 100)
setTimeout(() => {
reject(2)
}, 100)
})
複製代碼
上面代碼執行後 200ms
後 promise 的狀態爲 rejected
而不是 fulfilled
, 原則上 一旦改變就不能再改變了
若是在執行過程當中發生代碼錯誤, 那麼就用try catch
來捕獲錯誤, 並把錯誤轉成失敗狀態
, 就是把promise的狀態設爲rejected
狀態, 執行後續錯誤的回調
改造handle
方法
function handle(callback) {
if (this.state === 'pending') {
callbacks.push(callback)
return
}
const { fulfilled, rejected, resolve, reject } = callback
const cb = this.state === 'fulfilled' ? fulfilled : rejected
const next = this.state === 'fulfilled' ? resolve : reject
if (!cb) { // 沒有 回調 就直接進入下一個promise
next(value)
return
}
try {
const ret = cb(value)
// ①
resolve(ret) // 這裏始終是 resolve, 即時 state 是 rejected
} catch (error) {
callback.reject(error)
}allback.reject(e) //錯誤 直接執行失敗狀態回調
}
}
複製代碼
須要注意的就是, 上面 代碼 註釋 ①
處 始終是 resolve
的緣由是 和 寫的這篇Promise 上 所說的 這一級 執行錯誤回調, 可是返回值仍然是下一級的 resolved
回調 接收並執行
異常處理能夠直接使用 catch 來接受
this.catch = rejected => {
this.then(null, rejected) // 在 鏈式調用末尾再添加一個只有 rejected 的 then
}
複製代碼
這樣就實現了catch 功能, 若是前面then中都沒有 rejected
回調(由於值都爲 null
) , 會直接將值傳給最後catch(也就是最後的then)
Promise.resolve
的入參可能有如下幾種狀況:
// function oPromise(fn).then(...)
oPromise.resolve = function (value) {
if (state !== 'pending') return
if (value && (typeof value === 'object' && typeof value.then === 'function')) {
const { then } = value
return new oPromise(resolve => {
then(resolve) // ②
})
} else if (value) {
return new oPromise(resolve => resolve(value))
} else {
return new oPromise(resolve => resolve())
}
}
oPromise.resolve(1).then(res => console.log(res)) // 1
複製代碼
上面代碼中 ②
處的意思就是, 當傳入的參數(value) 是這樣的:
let value = new Promise(resolve => {
resolve(1)
}).then(res => {
return res + 1
})
Promise.resolve(value)
.then(res => console.log(res)) // 2
複製代碼
雖然 value
已是一個 promise 實例了, 貌似直接返回(如上面代碼中的①
), 而後再被後續的 then 註冊是沒有問題的. 可是 value 是一個失敗狀態
的 promise 實例呢:
let value = new Promise((resolve, reject) => {
reject(1)
}).then(res => {
return res + 1
}, err => {
console.log(err)
return err + 1
})
Promise.resolve(value).then(res => {
console.log(res)
}, err => {
console.log(err) // what?? 我是2???
})
複製代碼
若是像 ①
處那樣直接返回, 後續的 then 實例中的回調是會被執行 rejected
回調的, 這樣就是說, 我 oPromise.resolve 竟然返回的是一個失敗狀態的 promise ??? Oh, No
Promise.reject
與 Promise.resolve
相似, 區別在於 Promise.reject 始終返回一個狀態的rejected的Promise實例, 而 Promise.resolve 的參數若是是一個 Promise 實例的話, 返回的是參數對應的 Promise 實例, 因此狀態不必定
oPromise.reject = function (value) {
return new Promise(function(resolve, reject) {
reject(value)
})
}
複製代碼
入參是一個 Promise 的實例數組, 而後註冊一個 then 方法, 而後是數組中的 Promise 實例的狀態都轉爲 fulfilled 以後則執行 then 方法. 這裏主要就是一個計數邏輯, 每當一個 Promise 的狀態變爲 fulfilled 以後就保存該實例返回的數據, 而後將計數減一, 當計數器
變爲 0
時, 表明數組中全部 Promise 實例都執行完畢.
oPromise.all = function (arr) {
let args = Array.prototype.slice.call(arr)
return new Promise(function (resolve, reject) {
if (args.length === 0) return resolve([])
let remaining = args.length
function res(i, val) {
try {
if (val && (typeof val === 'object' || typeof val === 'function')) {
let then = val.then
if (typeof then === 'function') {
then.call(val, function (val) { // 這裏若是傳入參數是 promise的話須要將結果傳入 args, 而不是 promise實例
res(i, val)
}, reject)
return
}
}
args[i] = val
if (--remaining === 0) {
resolve(args)
}
} catch (ex) {
reject(ex)
}
}
for (let i = 0; i < args.length; i++) {
res(i, args[i])
}
})
}
複製代碼
有了 oPromise.all
的理解, oPromise.race
理解起來就更容易了. 它的入參也是一個 Promise 實例數組, 而後其 then 註冊的回調方法是數組中的某一個 Promise 的狀態變爲 fulfilled 的時候就執行. 由於 Promise 的狀態只能改變一次, 那麼咱們只須要把 Promise.race 中產生的 Promise 對象的 resolve 方法, 注入到數組中的每個 Promise 實例中的回調函數中便可.
oPromise.race = function (args) {
return new oPromise((resolve, reject) => {
for (let i = 0, len = args.length; i < len; i++) {
args[i].then(resolve, reject)
}
})
}
複製代碼
無論Promise最後的狀態如何 都要執行一些最後的操做. 咱們把這些操做放到 finally 中 也就是說 finally 註冊的函數是與 Promise 的狀態無關的 不依賴 Promise 的執行結果
function oPromise(fn) {
// ...
this.finally = function(done) {
this.then(() => {
done()
}, () => {
done()
})
}
// ...
}
複製代碼
之因此沒有吧 done 直接傳給 then 是由於 原版 Promise
的 finally
執行回調中並無傳入任何參數
偷偷地作了修改...
function oPromise(fn) {
this.state = 'pending'
this.value = null
let callbacks = []
this.then = function (fulfilled, rejected) {
return new oPromise((resolve, reject) => {
handle({
fulfilled: fulfilled,
rejected: rejected,
resolve,
reject
})
})
}
this.catch = function (rejected) {
this.then(null, rejected)
}
this.finally = function (done) {
this.then(() => {
done()
})
}
const handle = (callback) => {
if (this.state === 'pending') {
callbacks.push(callback)
return
}
const { fulfilled, rejected, resolve, reject } = callback
const cb = this.state === 'fulfilled' ? fulfilled : rejected
const next = this.state === 'fulfilled' ? resolve : reject
if (!cb) {
next(value)
return
}
try {
const ret = cb(value)
resolve(ret)
} catch (error) {
callback.reject(error)
}
}
const resolve = newValue => {
if (state !== 'pending') return
const fn = () => {
if (this.state !== 'pending') return
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
const then = newValue.then
if (typeof then === 'function') {
then.call(newValue, resolve)
return
}
}
this.state = 'fulfilled'
value = newValue
handleCb()
}
setTimeout(fn, 0)
}
const reject = newValue => {
if (state !== 'pending') return
const fn = () => {
if (this.state !== 'pending') return
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
const { then } = newValue
if (typeof then === 'function') {
then.call(newValue, reject)
return
}
}
value = newValue
this.state = 'rejected'
handleCb()
}
setTimeout(fn, 0)
}
const handleCb = _ => {
while (callbacks.length) {
const cb = callbacks.shift()
handle(cb)
}
}
fn(resolve, reject)
}
oPromise.resolve = function (value) {
if (value && value instanceof oPromise) {
return value
} else if (value && (typeof value === 'object' && typeof value.then === 'function')) {
const { then } = value
return new oPromise(resolve => {
then(resolve)
})
} else if (value) {
return new oPromise(resolve => resolve(value))
} else {
return new oPromise(resolve => resolve())
}
}
oPromise.race = function (args) {
return new oPromise((resolve, reject) => {
for (let i = 0, len = args.length; i < len; i++) {
args[i].then(resolve, reject)
}
})
}
oPromise.all = function (arr) {
let args = Array.prototype.slice.call(arr)
return new Promise(function (resolve, reject) {
if (args.length === 0) return resolve([])
let remaining = args.length
function res(i, val) {
try {
if (val && (typeof val === 'object' || typeof val === 'function')) {
let then = val.then
if (typeof then === 'function') {
then.call(val, function (val) {
res(i, val)
}, reject)
return
}
}
args[i] = val
if (--remaining === 0) {
resolve(args)
}
} catch (ex) {
reject(ex)
}
}
for (let i = 0; i < args.length; i++) {
res(i, args[i])
}
})
}
複製代碼
由於函數(function)中 this
指向問題, 我把全部的函數都換成了 箭頭函數
, 由於能夠直接使用 this
嘿嘿嘿....
原文出自 這裏, 大部分都是 copy 的, 嘿嘿. 可是加入了本身的理解, 嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿...