目次
//この記事では、基本的な約束を理解します。境界条件が多すぎることはテストではありません。主な実装プロセスとロジックを理解するだけです。
//次々に発生する問題について、原因とそれに対応する解決策を徐々に分析していきます
//前提として、Promise の基本的な使用法をマスターし、それに応じてコールバック関数を理解する必要があります。
//ステップ 1: 最も単純な Promise の暫定実装 (ステップごとに書き留めてください。Xiaobai はそれを理解できます。もちろん、いくつかのナンセンスを直接スキップすることもできます~~~)
1: まず公式の約束を分析する
//---首先分析官方的promise
const p1 = new Promise((resolve, reject) => {
resolve(2222)
reject(33333)
})
// 调用then方法 then 方法接收2个回调函数作为参数
p1.then(res => { //只会打印222, 因为promise的状态一但由pending的状态改变了 就无法再次改变
console.log("res1:", res)
}, err => {
console.log("err:", err)
})
//分析:
//promise のパラメータが関数を受け取り、その関数内のパラメータとして 2 つのコールバック関数を受け取り、resolve メソッドを呼び出していることがわかります。
// then メソッドを実行しています。then 関数は 2 つのパラメータを受け取りますが、どちらもコールバック関数であり、resolve(2222) を実行すると、then の最初のパラメータのコールバック関数に戻ります。
// request(33333) を実行した後、then- の第 2 パラメータのコールバック関数に移ります。
//1. 次に、予備的な書き込みを取得できます。
const PROMISE_STATUS_PENDING = 'pending' //定义三种状态常量
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING //1.初始化状态 pending
this.value = undefined //2.保存参数
this.error = undefined
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) { //3. 只有pending状态才可改变
this.status = PROMISE_STATUS_FULFILLED
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
}
})
executor(resolve, reject) //在new myPromise 时初始化立即调用传递过来的函数
}
}
2: 手書きの約束からメソッドの設計
//2. 解決または拒否を実行すると、then メソッドが呼び出されます (上記のコードに基づいて変更されました)
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
this.resfn(value)
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
this.errfn(error)
}
})
executor(resolve, reject)
}
then(resfn, errfn) { //4.then 方法接收2个回调函数作为 参数 : 第一个为成功的回调,第二个失败的回调
this.resfn = resfn
this.errfn = errfn
}
}
//(コードが最も基本的な実装であるかどうかを確認します) 次に、実装した Promise を実行して、それが正常であるかどうかを確認します
const p1 = new myPromise((解決、拒否) => {
解決(111)
})
p1.then(res => {
console.log(res);
}、エラー => {
})
//実行後、エラー メッセージが表示されます: this.resfn は関数ではありません
//それでは、なぜエラーが報告されるのでしょうか? コードの実行順序に関係するので、new myPromise()でresolveを実行するとresolveメソッドにたどり着きます。
//そして、その後のメソッド アヒルのスープは実行されないため、エラーが報告されます
//解決策: then を最初に実行させ、then メソッドが実行されるときに非同期キューに replace または拒否を追加します。
上記のコード:
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
//大家可能会想到使用setTimeout 然后0秒执行,但此处使用queueMicrotask 会更加合适
// setTimeout(() => {
// this.resfn(value)
// }, 0);
queueMicrotask(() => { //queueMicrotask: 主线程执行完毕之后立马执行
this.resfn(value)
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
queueMicrotask(() => {
this.errfn(error)
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) { //4.then 方法接收2个回调函数作为 参数 : 第一个为成功的回调,第二个失败的回调
this.resfn = resfn
this.errfn = errfn
}
}
//举一个栗子:
setTimeout(() => {
console.log('setTimeout');
}, 0);
queueMicrotask(() => {
console.log('queueMicrotask');
});
//实际运行
queueMicrotask
setTimeout
次に、以下を実行します。
const p1 = new myPromise((解決、拒否) => {
解決(111)
拒否(333333)
})
p1.then(res => { // 最終出力 1111
console.log(res);
}、エラー => {
コンソール.ログ(エラー);
})
これまでのところ、最も単純な実装は完了しています。その後、 then メソッドの最適化を続けます。
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = [] //1.多次调用then 时用数组 保存
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
queueMicrotask(() => {
this.value = value
this.resfns.forEach(fn => { //循环依次调用
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
queueMicrotask(() => {
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) { //将多次调用的then 方法保存到数组中,依次调用
this.resfns.push(resfn)
this.errfns.push(errfn)
}
}
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = [] //1.多次调用then 时用数组 保存
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.resfns.forEach(fn => {
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
this.status = PROMISE_STATUS_REJECTED
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) {
// 1.如果在then调用的时候, 状态已经确定下来,那么就可以直接调用
if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
resfn(this.value)
}
if (this.status === PROMISE_STATUS_REJECTED && errfn) {
errfn(this.error)
}
//2. 相反 对与已经确定的状态 就不需要在push 执行了
if (this.status === PROMISE_STATUS_PENDING) {
this.resfns.push(resfn)
this.errfns.push(errfn)
}
}
}
埋め込む:
const p1 = new myPromise((解決、拒否) => {
解決(111)
// 拒否(333333)
})
p1.then(res => {
console.log("res1:", res)
}、エラー => {
console.log("err1:", err)
})
// then メソッドを呼び出して複数回呼び出す
p1.then(res => {
console.log("res2:", res)
}、エラー => {
console.log("err2:", err)
})
// Promise の状態を決定した後、再度呼び出します
setTimeout(() => {
p1.then(res => {
console.log("res3:", res)
}、エラー => {
console.log("err3:", err)
})
}、1000)
結果:
レス1: 111
レス2: 111
レス3:111
3: 次にメソッドの最適化:
前の then メソッドでは、インスタンス化されたクラスを通じて複数の呼び出しが呼び出されます。
例えば:
p1.then(res => {
console.log("res1:", res)
}、エラー => {
console.log("err1:", err)
})
p1.then(res => {
console.log("res2:", res)
}、エラー => {
console.log("err2:", err)
})
この種の呼び出しは現在のコードで実現可能ですが、常にチェーンで呼び出される場合はどうなるでしょうか? くだらない話をしてコードに進みましょう。
const p1 = new myPromise((解決、拒否) => {
解決(111)
拒否(333333)
})
p1.then(res => {
console.log("最初に成功したコールバック:", res)
「2 回目の成功コールバック」を返す
}、エラー => {
console.log("err1:", err)
return "2 番目の失敗コールバック"
}).then(res => {
console.log("res2:", res)
}、エラー => {
console.log("err2:", err)
})
これで、私たちのコードは間違いなく機能しません (独自の環境でテストできます) 次に、コードの最適化を続けます。
最適化する前に、まず Wave を分析します (公式の Promise チェーン コールを例として使用して説明します)。
1.解決を実行する
2. then メソッドを呼び出します。 then メソッドによって返される値は実際には new myPromse((resolve,reject) =>solve("The 2 番目に成功したコールバック")) であり、戻り値はsolve() メソッドで渡されます。
したがって、「最初に成功したコールバック:」を出力した後、111、2 番目に成功したコールバックである「res2:」を再度出力します。
3. それでは、「err2:」の 2 番目の失敗コールバックはいつ出力されるのでしょうか? 初めて then を呼び出すときに例外をスローするだけでよく、err2 が実行されます。
例えば:
p1.then(res => {
console.log("最初に成功したコールバック:", res)
throw new Error("エラーメッセージ")
}、エラー => {
console.log("err1:", err)
return "2 番目の失敗コールバック"
}).then(res => {
console.log("res2:", res)
}、エラー => {
console.log("err2:", err)
})
結果:
"最初に成功したコールバック:", 111
「err2: エラー: エラーメッセージ
2. 拒否が実行される場合、拒否の 2 番目のコールバックで
エラー => {
console.log("err1:", err)
return "2 番目の失敗コールバック"
}
値が返された場合、次に console.log("res2:", res) または console.log("err2:", err) が実行されますか?
例えば:
const p1 = new myPromise((解決、拒否) => {
拒否(333333)
})
p1.then(res => {
console.log("最初に成功したコールバック:", res)
「2 回目の成功コールバック」を返す
}、エラー => {
console.log("err1:", err)
return "2 番目の失敗コールバック"
}).then(res => {
console.log("res2:", res)
}、エラー => {
console.log("err2:", err)
})
ここでは、console.log("res2:", res) が実際に実行されることを説明します。console.log("err2:", err) でロックされたコード ブロックを実行したい場合は、「the」を返す必要があります。 2 番目の失敗コールバック「実行するために例外をスローするように変更する」
栗:
p1.then(res => {
console.log("最初に成功したコールバック:", res)
「2 回目の成功コールバック」を返す
}、エラー => {
console.log("err1:", err)
throw new Error("エラーメッセージ")
}).then(res => {
console.log("res2:", res)
}、エラー => {
console.log("err2:", err)
})
ロジックは明確化され、コードで表現されています: (次のコードをコピーし、上記のケースで自分でテストしてより明確にすることができます)
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = []
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return //避免 调用resolve 后又调用 reject 多次执行
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.resfns.forEach(fn => {
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) {
return new myPromise((resolve, reject) => { //1. 直接new 一个mypromise 作为then 方法的返回值 既可实现 .then.then.thne.then.....等等链式调用
if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
try { //2. 异常处理:若成功则继续执行then链式调用 的第一个回调,失败则执行then 的第二个回调
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_REJECTED && errfn) {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
this.resfns.push(() => { //push 回调函数
try {
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err)
}
})
this.errfns.push(() => {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
})
}
})
}
}
//然后测试:
const p1 = new myPromise((resolve, reject) => {
// resolve(111)
reject(333333)
})
p1.then(res => {
console.log("res1:", res)
return "第二次的成功回调"
}, err => {
console.log("err1:", err)
throw new Error("err message")
// return "第二次的失败回调"
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
4: プロミス・キャッチ方式の設計
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = []
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return //避免 调用resolve 后又调用 reject 多次执行
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.resfns.forEach(fn => {
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) {
// 1.难点:利用抛错让下一个promise的catch帮忙处理 防止catch方法让链式调用断层
const defaultOnRejected = err => {
throw err
}
errfn = errfn || defaultOnRejected
return new myPromise((resolve, reject) => {
if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
try { //2. 异常处理:若成功则继续执行then链式调用 的第一个回调,失败则执行then 的第二个回调
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_REJECTED && errfn) {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
if (resfn) {
this.resfns.push(() => { //push 回调函数
try {
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err) //tips:****利用抛出的错误 使得下一个promise的catch帮忙处理
}
})
}
if (errfn) {
this.errfns.push(() => {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
})
}
}
})
}
catch (errfn) { //2.catch 方法
return this.then(undefined, errfn)
}
}
//接着测试代码:
const p1 = new myPromise((resolve, reject) => {
reject(333333)
})
p1.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
})
//结果:
err: 333333
5: Promise-finally メソッドの設計
const PROMISE_STATUS_PENDING = 'pending' //定义三种状态常量
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = []
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return //避免 调用resolve 后又调用 reject 多次执行
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.resfns.forEach(fn => {
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) {
// 1.利用抛错让下一个promise的catch帮忙处理 防止catch方法让链式调用断层
const defaultOnRejected = err => {
throw err
}
errfn = errfn || defaultOnRejected
const defaultOnFulfilled = value => {
return value
}
resfn = resfn || defaultOnFulfilled
return new myPromise((resolve, reject) => { //1. 直接new 一个mypromise 作为then 方法的返回值 既可实现 .then.then.thne.then.....等等链式调用
if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
try { //2. 异常处理:若成功则继续执行then链式调用 的第一个回调,失败则执行then 的第二个回调
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_REJECTED && errfn) {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
if (resfn) {
this.resfns.push(() => { //push 回调函数
try {
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err) //tips:****利用抛出的错误 使得下一个promise的catch帮忙处理
}
})
}
if (errfn) {
this.errfns.push(() => {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
})
}
}
})
}
catch (errfn) { //2.catch 方法
return this.then(undefined, errfn)
}
finally(fn) {
setTimeout(() => {
fn()
}, 0)
}
}
残りは比較的単純なクラスメソッドですので、ここでは説明しません~~さようなら~