JavaScriptはどうして自分でPromiseを書きますか?
26164 ワード
この文章をよく読んでください.簡単で機能的なPromiseを自分でカプセル化して、Promiseに対する理解を深めることができます.
提案:この文章を読む前に、あなたにお願いします. ES 6の文法を知っています. Promises/A+仕様を知る[Promises/A+] はPromise を使用します.
文章は長くて、コードの一貫性が強くて、簡単から始めて、読者は必要に応じて選んで読むことができます.
一.一番簡単なPromise
二.非同期のPromise
1.then()は非同期である
Promiseの中のthen関数のコードは非同期で実行されていることを知っていますが、私達が書いたのはこれではなく、検証できます.
明らかにこのコードは同期して実行されますが、私たちが欲しい出力順序は
2.状態関数非同期実行
状態関数
三.Promiseのエラーキャプチャー
エラーが発生した場合、
実行中にエラーが発生した以上、
then関数は二つの特性があります. then関数が実行された後、新しいPromiseのインスタンス に戻る. then関数は、チェーン式で を呼び出すことができます.
1.thenのチェーン呼び出し
次の方
2.thenの処理関数の戻り値はPromiseの例ではない.
文字どおりにコードを書けば
ps:
3.thenの処理関数の戻り値はPromiseの例である.
五.一部のPromise上の方法(直接コード)
そうだ、もう一つの方法があります.
機能の説明が長すぎて、わからないのは阮一峰先生のPromise.allに対する紹介を見ることができます.
上のコードも完璧ではありません.細かい問題は解決されていませんが、核心機能も完成しています.以下には少し完全なコードを提供します.
私の文章を読んでくれてありがとうございます.勉強になりますように.
提案:この文章を読む前に、あなたにお願いします.
文章は長くて、コードの一貫性が強くて、簡単から始めて、読者は必要に応じて選んで読むことができます.
一.一番簡単なPromise
class Promise {
constructor (executor) {
if (typeof executor !== 'function')
throw new TypeError(`Promise resolver ${executor} is not a function`)
/* */
this.state = 'pending'
this.value = undefined
this.reason = undefined
/*
resolve, reject
1.pending -> fulfilled, pending -> rejected
2. Promise this.value = value, this.reason = reason
*/
const resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
}
}
const reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
}
}
executor(resolve, reject)
}
then (onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value)
}
if (this.state === 'rejected') {
onRejected(this.reason)
}
}
}
ps:テストツールはvsCodeのQuokkaプラグインです.Promise
の状態関数res
およびrej
によれば、then
の処理関数onFulfilled
およびonRejected
を実行することに対応する.二.非同期のPromise
1.then()は非同期である
Promiseの中のthen関数のコードは非同期で実行されていることを知っていますが、私達が書いたのはこれではなく、検証できます.
明らかにこのコードは同期して実行されますが、私たちが欲しい出力順序は
0 2 1
です.だから、setTimeoutを使ってこの非同期ステップをシミュレートすることができます.class Promise {
constructor (executor) { ... }
then (onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
/* setTimeout */
setTimeout(() => {
onFulfilled(this.value)
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
onRejected(this.reason)
}, 0);
}
}
}
OK、完璧に欲しいものを手に入れます.2.状態関数非同期実行
状態関数
res/rej
は非同期のために実行される時、私達は見ることができます.then
は無反応です.
なぜですかthen
関数が実行されると、res
は非同期で実行されるため、状態はまだpending
です.then
関数の中にはまだ状態がpending
の処理がありません.コードを修正してください.class Promise {
constructor (executor) {
...
/* , */
this.resolveCallBackList = []
this.rejectCallBackList = []
const resolve = value => {
if (this.state === 'pending') {
...
/* , */
this.resolveCallBackList.length > 0
&& this.resolveCallBackList.forEach(e => e())
}
}
const reject = reason => {
if (this.state === 'pending') {
...
this.rejectCallBackList.length > 0
&& this.rejectCallBackList.forEach(e => e())
}
}
...
}
then (onFulfilled, onRejected) {
...
/* pending , */
if (this.state === 'pending') {
onFulfilled && this.resolveCallBackList.push( () => {
onFulfilled(this.value)
})
onRejected && this.rejectCallBackList.push( () => {
onRejected(this.reason)
})
}
}
}
このように、状態関数が非同期で実行される場合にも処理が可能であり、状態がpending
である場合には、処理関数onFulfilled/onRejected
を保存し、状態関数res/rej
が実行されると、対応する処理関数が自動的に実行されるということが分かります.三.Promiseのエラーキャプチャー
エラーが発生した場合、
Promise
はエラーを報告しないで、失敗した処理関数then
によってエラーを捉えて処理しています.もし私達が自分で書いたPromise
にエラーが発生したら、予期せず直接エラーを報告します.このように.実行中にエラーが発生した以上、
try/catch
を使ってエラーを捕まえられます.class Promise {
constructor (executor) {
...
/* try/catch , reject, rejected */
try {
executor(resolve, reject)
} catch (error) {
this.state === 'pending' && reject(error)
}
}
then (onFulfilled, onRejected) { ... }
}
四.then関数詳細then関数は二つの特性があります.
1.thenのチェーン呼び出し
new Promise(res => res(0))
.then(value => {
console.log(value) // 0
return `1 fulfilled`
})
.then(value => {
console.log(value) // 1 fulfilled
})
then
の関数が実行された後、Promise
の例に戻り、Promise
によって次のthen
の関数が決定され、このthen
の例に従って、対応する処理関数が実行され、図を描く.次の方
Promise
の実行は前のものに依存する.then
は、リターンを実行する.then
例で、これはPromise
の例のデータは前のデータによる.Promise
の処理関数then
のonFulfilled/onRejected
決定2.thenの処理関数の戻り値はPromiseの例ではない.
文字どおりにコードを書けば
class Promise {
constructor (executor) { ... }
then (onFulfilled, onRejected) {
/* Promise */
const newPromise = new Promise ( (res, rej) => {})
...
return newPromise
}
}
このように書くと意味がないので、
に戻る状態は常にPromise
であり、状態関数pending
を実行していないので、res/rej
関数のチェーンコールもできない.then
のnew Promise(executor)
関数は同期して実行されるので、このように書いてもいいです.class Promise {
constructor (executor) { ... }
then (onFulfilled, onRejected) {
const newPromise = new Promise ( (res, rej) => {
/*
,
res/rej Promise
*/
if (this.state === 'fulfilled') {
setTimeout(() => {
/* */
const value = onFulfilled(this.value)
/* Promise */
res(value)
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
const reason = onRejected(this.reason)
res(reason)
}, 0);
}
if (this.state === 'pending') {
onFulfilled && this.resolveCallBackList.push( () => {
const value = onFulfilled(this.value)
res(value)
})
onRejected && this.rejectCallBackList.push( () => {
const reason = onRejected(this.reason)
res(reason)
})
}
})
return newPromise
}
}
パカパカexecutor
のチェーン呼び出しが完了しました.ps:
then
の処理関数戻り値がthen
の例ではない場合、Promise
またはfullfilled
は、次のrejected
の関数を実行するthen
である.3.thenの処理関数の戻り値はPromiseの例である.
onFulfilled
の処理関数の戻り値がthen
の例である場合、次のPromise
の関数の実行は、このthen
の例によって全部決定されるので、Promise
の関数を使って、戻り値の種類を判断し、対応する場合を処理する必要がある.class Promise {
constructor (executor) { ... }
/*
promise -> Promise
target -> then
res/rej -> Promise
*/
checkReturnValueIfPromise (promise, target, res, rej) {
if (target instanceof promise) {
/*
Promise
then , Promise
Promise
,
target.then( value => {
res(value)
}, reason => {
rej(reason)
} )
*/
target.then(res, rej)
} else {
res(target)
}
}
then (onFulfilled, onRejected) {
const newPromise = new Promise ( (res, rej) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
const value = onFulfilled(this.value)
/* */
this.checkReturnValueIfPromise(Promise, value, res, rej)
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
const reason = onRejected(this.reason)
this.checkReturnValueIfPromise(Promise, reason, res, rej)
}, 0);
}
if (this.state === 'pending') {
onFulfilled && this.resolveCallBackList.push( () => {
const value = onFulfilled(this.value)
this.checkReturnValueIfPromise(Promise, value, res, rej)
})
onRejected && this.rejectCallBackList.push( () => {
const reason = onRejected(this.reason)
this.checkReturnValueIfPromise(Promise, reason, res, rej)
})
}
})
return newPromise
}
}
異歩でも欠点は一つもない.五.一部のPromise上の方法(直接コード)
そうだ、もう一つの方法があります.
checkReturnValueIfPromise
と同様の方法then
があります.この方法はcatch
の状態を専門に扱うものです.コードは一言だけです.class Promise {
constructor () { ... }
then () { ... }
catch (onRejected) {
this.then(undefined, onRejected)
}
}
1.Promise.resoverejected
状態のfulfilled
例を返します.class Promise {
constructor () { ... }
then () { ... }
catch () { ... }
static resolve (value) {
return new Promise( res => res(value))
}
}
2.Promise.rejectPromise
状態のrejected
例を返します.class Promise {
constructor () { ... }
then () { ... }
catch () { ... }
static resolve () { ... }
static reject (reason) {
return new Promise( (undefined, rej) => rej(reason))
}
}
3.Promise.racePromise
の例を受信した配列Promise
は、promiseArray
の例に戻り、Promise
の中で最も速いPromise
の実施例によって決定される.class Promise {
constructor () { ... }
then () { ... }
catch () { ... }
static resolve () { ... }
static reject () { ... }
static race (promiseArray) {
return new Promise ( (res, rej) => {
promiseArray.forEach( promise => {
promise.then(res, rej)
})
})
}
}
4.Promise.all機能の説明が長すぎて、わからないのは阮一峰先生のPromise.allに対する紹介を見ることができます.
class Promise {
constructor () { ... }
then () { ... }
catch () { ... }
static resolve () { ... }
static reject () { ... }
static race () { ... }
static all (promiseArray) {
let count = 0,
resultArray = []
return new Promise( (res, rej) => {
promiseArray.forEach( promise => {
promise.then( value => {
count++
resultArray.push(value)
if (count === promiseArray.length) {
res(resultArray)
}
}, reason => {
rej(reason)
})
})
})
}
}
六.完全なコード(注釈付き)上のコードも完璧ではありません.細かい問題は解決されていませんが、核心機能も完成しています.以下には少し完全なコードを提供します.
class Promise {
constructor (executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} must be a function`)
}
this.state = 'pending'
this.value = undefined
this.reason = undefined
/* , then() (onFulfilled, onRejected) */
this.resolveCallBackList = []
this.rejectCallBackList = []
/**
* @method resolve
* @param {string} value
* @function , ,
* @returns {undefined}
*/
const resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
this.resolveCallBackList.length > 0 && this.resolveCallBackList.forEach(e => e())
}
}
/**
* @method reject
* @param {string} reason
* @function , ,
* @returns {undefined}
*/
const reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
this.rejectCallBackList.length > 0 && this.rejectCallBackList.forEach(e => e())
}
}
/* Promise , rejected */
try {
executor(resolve, reject)
} catch (error) {
this.state === 'pending' && reject(error)
}
}
/**
* @method checkLastThenReturn
* @param {Promise} promise Promise
* @param {*} target then (onFulfilled/onRejected)
* @param {function} res Promise
* @param {function} rej Promise
* @function then() Promise
*/
checkLastThenReturn (promise, target, res, rej) {
if (target instanceof promise) {
/* target Promise , , Promise */
target.then(res, rej)
} else {
/* , */
res(target)
}
}
/**
* @method then
* @param {function} onFulfilled fulfilled
* @param {function} onRejected rejected
* @function Promise , Promise , then()
* @returns {Promise} Promise
*/
then (onFulfilled, onRejected) {
/* then() , */
if (!onFulfilled && !onRejected && this.state === 'pending') return this
/* then(onFulfilled) rejected , Promise */
if (!onRejected && this.state === 'rejected') return Promise.reject(this.reason)
/* then() , */
if (!onFulfilled && !onRejected) {
onFulfilled = value => value
onRejected = reason => reason
}
/**
* @method returnPromise
* @param {function} res Promise fulfilled
* @param {function} rej Promise rejected
* @function , then() , res rej, Promise
*/
const returnPromise = new Promise( (res, rej) => {
/* then() */
if (this.state === 'fulfilled') {
/* setTimeout then() */
setTimeout(() => {
/* , Promise rejected */
try {
const value = onFulfilled(this.value)
this.checkLastThenReturn(Promise, value, res, rej)
} catch (error) {
rej(error)
}
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
try {
const reason = onRejected(this.reason)
this.checkLastThenReturn(Promise, reason, res, rej)
} catch (error) {
rej(error)
}
}, 0);
}
/* then() */
if (this.state === 'pending') {
onFulfilled && this.resolveCallBackList.push( () => {
try {
const value = onFulfilled(this.value)
this.checkLastThenReturn(Promise, value, res, rej)
} catch (error) {
rej(error)
}
})
if (onRejected) {
this.rejectCallBackList.push( () => {
try {
const reason = onRejected(this.reason)
this.checkLastThenReturn(Promise, reason, res, rej)
} catch (error) {
rej(error)
}
})
} else {
/* onRejected, Promise */
this.rejectCallBackList.push( () => {
// Promise.reject(this.reason).catch(rej)
rej(this.reason)
})
}
}
})
return returnPromise
}
/**
* @method catch
* @param {function} onRejected
* @function onFulfilled then()
* @returns {Promise}
*/
catch (onRejected) {
this.then(undefined, onRejected)
}
/**
* @method reject
* @param {string} reason
* @function Promise
* @returns {new Promise}
*/
static reject (reason) {
return new Promise( (undefined, rej) => rej(reason))
}
/**
* @method resolve
* @param {string} value
* @function Promise
* @returns {new Promise}
*/
static resolve (value) {
return new Promise( res => res(value))
}
/**
* @method race
* @param {array} promiseArray Promise
* @function Promise Promise
* @returns Promise , Promise Promise
*/
static race (promiseArray) {
return new Promise ( (res, rej) => {
promiseArray.forEach( promise => {
promise.then(res, rej)
})
})
}
/**
* @method all
* @param {array} promiseArray Promise
* @function Promise Promise , , Promise
* @returns Promise
*/
static all (promiseArray) {
let count = 0,
resultArray = []
return new Promise( (res, rej) => {
promiseArray.forEach( promise => {
promise.then( value => {
count++
resultArray.push(value)
if (count === promiseArray.length) {
res(resultArray)
}
}, reason => {
rej(reason)
})
})
})
}
}
七.結語私の文章を読んでくれてありがとうございます.勉強になりますように.