Promise thenチェーンの落とし穴
13246 ワード
一、thenチェーンにおいてPromiseオブジェクトの状態は、その前のPromiseオブジェクトの状態と直接関係がない.
すなわち、
1.2解析 は 1.
プロミゼ1が
このとき
3.1
3.2もし
前の規則が成立する前提は
大体次の流れを実行します.は、 もし もし thenableオブジェクトは一般的ではないと思いますが、Promise配置関数の実参関数はthenableオブジェクトのthenメソッドルールに従います.
3.4その他の場合(は、冒頭のDEMOのように トランスポート 二、
Promiseがどんな状態にあっても は、
handle in finally
waiting 3 s…
Rejeced:handle in then reason=reason from finally calback
2.2実現 Promise.resove(value)において、実参加 参照はgit Hubメモ:JS-ES 6-Promise/Promise A+仕様 から整理されています.
すなわち、
var promise2 = promise1.then(onFulfilled, onRejected)
式におけるpromise2
の状態はpromise1
と直接関係がない.// promise1
var promise1 = new Promise((resolve, reject) => {
reject('Rejected')
})
// promise2 , `promise1.then`
var promise2 = promise1.then(value => {
console.log(`[promise1]: fulfilled, value='${value}'`)
}, reason => {
console.log(`[promise1]: rejected, reason='${reason}'`)
})
promise2.then(value => {
console.log(`[promise2]: fulfilled, value='${value}'`)
}, reason => {
console.log(`[promise2]: rejected, reason='${reason}'`)
})
出力:[promise1]: rejected, reason='Rejected'
[promise2]: fulfilled, value='undefined'
promise 1はrejected
になりましたが、promise 2はfulfilled
状態です.1.2解析
promise2 = promise1.then(onFulfilled, onRejected)
2点を覚えてください:promise1
の最終状態は、onFulfilled
またはonFulfilled
コールバック関数を決定するだけである.promise2
の最終状態はonFulfilled
OR onRejected
のコールバック関数の影響を受けただけです.onFulfilled
/onRejected
のコールバック関数がありますか?onFulfilled
/onRejected
コールバック関数実行中に異常が発生しましたか?onFulfilled
/onRejected
コールバック関数の戻り値x
.onFulfilled
/onRejected
コールバック関数がない場合:プロミゼ1が
fulfilled
(rejected
)であり、onFulfilled
(onRejected
)のコールバック関数がない場合、プロミゼ2はプロミゼ1の状態(すなわちpromise2
の状態がpromise1
の状態と一致する)を採用する.var promise1 = new Promise((resolve, reject) => {
reject('Rejected')
})
// promise1 `onRejected`
var promise2 = promise1.then()
promise2.then(value => {
console.log(`[promise2]: fulfilled, value='${value}'`)
}, reason => {
console.log(`[promise2]: rejected, reason='${reason}'`)
})
出力:[promise2]: rejected, reason='Rejected'
var promise1 = new Promise((resolve, reject) => {
resolve('Fulfilled')
})
// promise1 `onFulfilled`
var promise2 = promise1.then()
promise2.then(value => {
console.log(`[promise2]: fulfilled, value='${value}'`)
}, reason => {
console.log(`[promise2]: rejected, reason='${reason}'`)
})
出力:[promise2]: fulfilled, value='Fulfilled'
2.onFulfilled
/onRejected
の実行中に異常が発生した場合:このとき
Reject
promise 2対象となり、異常をreasonとする.var promise1 = new Promise((resolve, reject) => {
resolve('Fulfilled')
})
// promise1 `onFulfilled`
var promise2 = promise1.then(() => {
throw new Error('A Error in onFulfilled func')
})
promise2.then(value => {
console.log(`[promise2]: fulfilled, value='${value}'`)
}, reason => {
console.log(`[promise2]: rejected, reason='${reason}'`)
})
出力:[promise2]: rejected, reason='Error: A Error in onFulfilled func'
3.OFulfilled/onRejeced関数の戻り値がxならば:onFulfilled
/onRejected
関数が正常に実行された場合、戻り値はx
である(指定された戻り値が表示されていない場合、xはundefined
である).3.1
x
がPromiseオブジェクトである場合、promise2
はx
の状態を採用する.var promise1 = new Promise((resolve, reject) => {
resolve('Fulfilled')
})
// promise1 `onFulfilled` `promise1`
var promise2 = promise1.then(() => {
return promise1;
})
promise2.then(value => {
console.log(`[promise2]: fulfilled, value='${value}'`)
}, reason => {
console.log(`[promise2]: rejected, reason='${reason}'`)
})
出力:[promise2]: fulfilled, value='Fulfilled
このときpromise2
とpromise1
は同じ状態と同じvalueを有する.3.2もし
x
とpromise2
が等しいなら、TypeError
の異常を投げる:前の規則が成立する前提は
x
とpromise2
が等しくないということです.x
およびpromise2
が等しい場合は、TypeError
が異常であり、promise2
のオブジェクトrejectd状態であるリリースがスローされる.var promise1 = new Promise((resolve, reject) => {
resolve('Fulfilled')
})
// promise1 `onFulfilled` `promise2`
var promise2 = promise1.then(() => {
return promise2;
})
promise2.then(value => {
console.log(`[promise2]: fulfilled, value='${value}'`)
}, reason => {
console.log(`[promise2]: rejected, reason='${reason}'`)
})
出力:[promise2]: rejected, reason='TypeError: Chaining cycle detected for promise #'
3.3 x
がthenableオブジェクトである場合:大体次の流れを実行します.
var then = x.then;
then.call(x, resolve, reject);
このプロセスはPromiseResolaveThe nable Jobとも呼ばれる.x.then
の内部でresolve(y)
を先に呼び出した場合、[[Resolive](promise,y)を実行する.この時、新たなPromise解析プロセスが開始されます.y
万はx
と同じではなく、無限再帰が発生します.x.then
の内部にrejected(y)
が先に呼び出されたら、promise2
もrejected
になり、yをreasonとする.x.then
内部に異常が先行したら、promise2
もrejected
になり、異常をreasonとする.var thenableA = {
name: 'thenableA',
then: function (resolve, reject) {
console.log(`I'am ${this.name}`);
resolve(this.name)
}
}
new Promise((resolve, reject) => {
console.log('create promise1');
resolve(thenableA)
}).then(() => {
console.log('promise1 fulfilled');
})
// promise1 `onFulfilled` thenableA
new Promise(resolve => {
console.log('create promise2');
resolve();
}).then(() => {
console.log('promise2 fulfilled');
})
出力:create promise1
create promise2
I'am thenableA
promise2 fulfilled
promise1 fulfilled
promise2 fulfilled
より前に、PromiseResolaveThe nable Jobプロセスは、他のコンテキストコードの解析後に、then方法の解析が行われることを保証するために、jobとしてマイクロタスクキューに参加する必要があるからである.3.4その他の場合(
promise1 fulfilled
はPromiseオブジェクトでもx
オブジェクトでもない):thenable
はful filledになり、promise2
をそのvalueとする.x
がpromise1
になっても、rejected
はpromise2
の状態である.fulfilled
のpromise1
コールバック関数の戻り値onRejected
はx
であるので、undefined
のvalueはpromise2
である.undefined
を行うには、x
の関数に表示されているリターンonFulfilled
を表示しなければならない.x
はpromsie.finally(onFinally)
と同等ではない.Promiseがどんな状態にあっても
promsie.then(onFinally, onFinally)
コールバック関数をトリガするが、onFinally
はpromsie.finally(onFinally)
と同等ではない.// Resolve
var promise2 = Promise.resolve(2)
.then(() => {});
promise2.then( value => {
console.log(`Fulfilled, value=${value}`) // value undefined
})
var promise2 = Promise.resolve(2)
.finally( () => {});
promise2.then( value => {
console.log(`Fulfilled, value=${value}`) // value 2
})
// Reject
var promise2 = Promise.reject(2)
.then(() => {}, () => {})
// promise2 fulfilled
promise2 .then(value => {
console.log(`Fulfilled, value=${value}`) // // value undefined
}, reason => {
console.log(`Rejected, reason=${reason}`)
})
var promise2 = Promise.reject(2).finally(() => {}, () => {});
// promise2 rejected
promise2 .then(value => {
console.log(`Fulfilled, value=${value}`)
}, reason => {
console.log(`Rejected, reason=${reason}`) // reason 2
})
promsie.then(onFinally, onFinally)
コールバック関数にはパラメータがありません.Promiseの終状態がfulfilledなのかそれともrejectなのかが分かりません.onFinally
方法の原則は、finally
チェーンの実行プロセスを変更しないことである.(finally方法の内部に例外がない限り)、上記の例ではthen
方法は前後のthenチェーンが存在しないように見える.Promise.reject('failed')
.finally(() => {
console.log('handle in finally')
return 'Hi i am finally'; // value, Promise rejected
})
.then(value => {
console.log(`Fullfilled: handle in then value=${value}`)
}, reason => {
console.log(`Rejected: handle in then reason=${reason}`) //
})
finally
のコールバック関数がPromiseである場合にも、このPromiseオブジェクトが最終状態に入った後に、後のfinally
チェーンが実行される.Promise.reject('failed')
.finally(() => {
console.log('handle in finally')
console.log('waiting 3s ...')
return new Promise(resolve => { // 3s
setTimeout(resolve, 3000) // fullfilled, finally Promise rejected
})
})
.then(value => {
console.log(`Fullfilled: handle in then value=${value}`)
}, reason => {
console.log(`Rejected: handle in then reason=${reason}`)
})
then
のコールバック関数が異常を投げた場合、またはfinally
のPromiseに戻った場合、前のPromiseの終状態valueまたはreason Promise.reject('failed')
.finally(() => {
console.log('handle in finally')
console.log('waiting 3s ...')
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('reason from finally callback ')
}, 3000)
})
})
.then(value => {
console.log(`Fullfilled: handle in then value=${value}`)
}, reason => {
console.log(`Rejected: handle in then reason=${reason}`)
})
の出力を差し替えます.handle in finally
waiting 3 s…
Rejeced:handle in then reason=reason from finally calback
rejected
の行動は素晴らしいです.最後にやはりfinally
チェーンの最後に書いてください.コードの読み取り障害を防ぐために.then
方法は、原則として、前のPromiseオブジェクトの結果を伝達するが、エラーが発生したら、後のfinally
チェーンの最新のエラーを教えてくれる.then
Promise.prototype.finally = function(onFinally) {
return this.then(value => {
return Promise.resolve(onFinally())
.then(
() => value, // Promise `value`
reason => {
throw reason // , `reason`
});
}, reason => {
return Promise.resolve(onFinally())
.then(
() => {
throw reason // Promise `reason`
},
reason => {
throw reason // , `reason`
});
})
}
Promise.prototype.finally(onFinally)
のコールバックは透過性の新しいonRejected
であるので、reason
のコールバック関数を省き、即ち、書き方を簡略化することができる.Promise.prototype.finally = function(onFinally) {
return this.then(value => {
return Promise.resolve(onFinally())
.then(() => value) // Promise `value`;
}, reason => {
return Promise.resolve(onFinally())
.then(
() => {
throw reason // Promise `reason`
});
})
}
三、onRejected
は、Promise.resolve(value)
と同等ではない.new Promise(resolve => {
resolve();
})
.then(() => {
console.log(1)
})
Promise.resolve()
.then(() => {
console.log(2)
})
出力結果:1->2、new Promise(resolve => resolve(value))
をPromiseオブジェクトに変更してみます.var p = new Promise(resolve => {
resolve();
});
new Promise(resolve => {
resolve(p);
})
.then(() => {
console.log(1) // EventLoop
})
Promise.resolve(p)
.then(() => {
console.log(2)
})
出力結果が変わった.value
がPromiseオブジェクトである場合(thenableオブジェクトを含まない)、直接に参加に戻る.value
において、new Promise(resolve => resolve(value))
がPromiseまたはthenableオブジェクトである場合、PromiseResolaveThe nable Jobが生成され、非同期jobが増加する.