手書きPromise/A+仕様
105518 ワード
ソース:https://github.com/dream2023/blog/tree/master/promise
Es 6の知識:http://es6.ruanyifeng.com
この問題:https://juejin.im/post/59bfe84351882531b730bac2
Promise基礎教育:https://www.imooc.com/learn/949
Promise/A+仕様原文:https://promisesaplus.com/
Promise/A+仕様訳文:http://www.ituring.com.cn/article/66566
参考記事BATフロントエンドクラシック面接質問:史上最も詳細な手書きPromiseチュートリアル:https://juejin.im/post/5b2f02cd5188252b937548ab
参考記事手書きでPromise/A+仕様を満たすPromiseを実現する:https://www.jianshu.com/p/8d5c3a9e6181
vscodeエディタ:https://code.visualstudio.com/
vscodeコード実行プラグインcode-runner:https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner
非同期を解決するいくつかの方法 callback(コールバック関数) generator+coライブラリ Promise async + await
次はPromiseを手書きで書きます
BATフロントエンドクラシック面接問題:史上最も詳細な手書きPromiseチュートリアル
私たちの仕事ではpromiseを用いて非同期コールバック問題を解決することは避けられない.普段使っている多くのライブラリやプラグインにはaxios、fetchなどのpromiseが使われています.でもpromiseはどうやって書いたか知っていますか?
恐れないで~ここにpromisesa+仕様があって、10元安く売ってあげました.
1、Promiseの声明
まずpromiseはクラスに違いありません.classで宣言します. executorにはresolve(成功)、reject(失敗)という2つのパラメータがあります. resolveとrejectは実行可能なので、関数です.letで宣言します.
基本状態の解決
秘籍はPromiseに規定されています. Promiseには3つの状態(state)pending、fulfilled、rejected が存在する. pending(待機状態)は初期状態であり、fulfilled(成功状態)およびrejected(失敗状態) に変換することができる.が成功した場合、他の状態に移行することはできず、変更不可能な値(value) が必要である.が失敗した場合、他の状態に移行することはできず、変更できない原因(reason) が必要である. executor関数エラーが発生した場合、reject()を直接実行します.
そこで、以下のコードを入手しました.
thenメソッド
秘籍規定:Promiseにはthenという方法があり、中には2つのパラメータがあります:onFulfilled,onRejected,成功には成功の値があり、失敗には失敗の原因がありますステータスstateがfulfilledの場合、onFulfilledが実行され、this.valueに渡されます.ステータスstateがrejectedの場合、onRejectedが実行され、this.reason に転送されます. onFulfilled,onRejected彼らが関数である場合、fulfilled,rejectedの後に呼び出される必要があります.valueまたはreasonは、彼らの最初のパラメータ として順次使用されます.
これで武学が完成し、江湖の雑毛に対処できるようになったが、settimeout付きの江洋大盗には仕方がない.
非同期実装の解決
簡単な同期コードは基本的に実現できますが、resolveがsetTomeout内で実行され、thenのstateまたはpending待機状態が必要です.then呼び出し時に、成功と失敗をそれぞれの配列に保存し、rejectまたはresolveを呼び出すと、それらを呼び出す必要があります.
サブスクリプションのパブリッシュと同様に、then内の2つの関数を先に格納します.1つのpromiseに複数のthenがあるため、同じ配列内に存在します.
成功または失敗した場合、forEachはそれらを呼び出します.
チェーンコールの解決
私のドアはよく
Promise中継状態は変更できないため、チェーン呼び出しを実現するにはthenメソッドの実行後の戻り値が新しいインスタンスである必要があります.
promiseのチェーン呼び出し(2つのケースに分けて議論)
promiseのthenメソッドはその後もpromiseオブジェクトを返します
例は次のとおりです.
nodeを使用して実行した結果は次のとおりです.
結果から、thenメソッドを使用するとpromiseオブジェクトが返され、thenメソッドで呼び出し続けることができ、取得したパラメータが前のthenメソッドreturnの内容であることがわかります.
上記の例は、1回目のthen以降に返されるpromiseではrejectの状態は現れないが、1回目のthen以降にrejectの状態を含む可能性のあるpromiseを手動で返すと、エラーチェーン呼び出し中のエラー処理が大きな問題になる.2回目のthenはエラー処理メカニズムを持たないため、これはpromiseのreject状態に対応するエラー処理メカニズムを直接書くことを要求する.
Promiseのresove,reject,all,raceメソッド実装
後ろの部分はまだはっきり理解していません(続き..)
1、チェーン式を達成するために、最初のthenでpromiseをデフォルトで返します.秘籍はthenの中で新しいpromiseを返す方法を規定しています.promise 2:このpromise 2が返す値を次のthenの に渡す通常の値を返すと、次のthenの に通常の値が渡される.
2、私たちが最初のthenの中で
秘籍ではonFulfilled()またはonRejected()の値、すなわち最初のthenが返す値をxと規定し、xを判断する関数をresolvePromiseと呼ぶまず、xがpromiseかどうかを見ます. promiseであれば、その結果を取って、新しいpromise 2が成功した結果として 通常値であればpromise 2として直接成功した結果 .だからxとpromise 2 を比較する resolvePromiseのパラメータにはpromise 2(デフォルトで返されるpromise)、x(私たち自身 があります. resolveとrejectはpromise 2の です
resolvePromise関数の完了
秘籍はコードを規定して、異なるpromiseコードを互いに組み合わせて、resolvePromiseと言います x==promise 2の場合、ループ参照が発生し、自分で完了するのを待つ場合、「ループ参照」エラー が報告されます.
1、判断x Otherwise, if x is an object or function,Let then be x.then xはnull ではありません xは通常値直接resolve(x) xはオブジェクトまたは関数(promiseを含む)であり、 である.はthen を宣言した thenを取り間違えたらreject() を歩く thenが関数である場合、callでthenが実行され、最初のパラメータはthisであり、その後は成功したコールバックと失敗したコールバック である.正常なコールバックがpormiseである場合、再帰的に解析を継続する3、成功と失敗は1つしか呼び出せないので、 の複数回の呼び出しを防止するためにcalledを設定する.
その他の問題の解決
1、秘籍規定onFulfilled、onRejectedはすべてオプションのパラメータで、もし彼らが関数でなければ、無視しなければならない. onFulfilledは通常の値を返し、成功した場合は に直接等しい. onRejectedは通常の値を返します.失敗した場合、value=>valueに直接等しいと、次のthenのonFulfilledに駆け込むので、エラー を解決する onFulfilledまたはonRejectedが間違っている場合は、reject() に直接戻ります.
大功を成し遂げる
ついでにcatchとresolve、reject、race、allメソッドを添付します
私たちのpromiseが正しいかどうかを検証する方法
1、先に下記のコードを付けます
2、npmにはpromises-aplus-testsプラグインnpm i promises-aplus-tests-gがあり、macユーザーの最前線にsudoを加えることができる.
3、コマンドラインpromises-aplus-tests[jsファイル名]で検証できる
Es 6の知識:http://es6.ruanyifeng.com
この問題:https://juejin.im/post/59bfe84351882531b730bac2
Promise基礎教育:https://www.imooc.com/learn/949
Promise/A+仕様原文:https://promisesaplus.com/
Promise/A+仕様訳文:http://www.ituring.com.cn/article/66566
参考記事BATフロントエンドクラシック面接質問:史上最も詳細な手書きPromiseチュートリアル:https://juejin.im/post/5b2f02cd5188252b937548ab
参考記事手書きでPromise/A+仕様を満たすPromiseを実現する:https://www.jianshu.com/p/8d5c3a9e6181
vscodeエディタ:https://code.visualstudio.com/
vscodeコード実行プラグインcode-runner:https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner
非同期を解決するいくつかの方法
次はPromiseを手書きで書きます
BATフロントエンドクラシック面接問題:史上最も詳細な手書きPromiseチュートリアル
私たちの仕事ではpromiseを用いて非同期コールバック問題を解決することは避けられない.普段使っている多くのライブラリやプラグインにはaxios、fetchなどのpromiseが使われています.でもpromiseはどうやって書いたか知っていますか?
恐れないで~ここにpromisesa+仕様があって、10元安く売ってあげました.
1、Promiseの声明
まずpromiseはクラスに違いありません.classで宣言します.
new Promise((resolve, reject)=>{})
のため、パラメータ(関数)が渡され、秘籍ではexecutorと呼ばれ、渡されて実行されます. class Promise{
//
constructor(executor){
//
let resolve = () => { };
//
let reject = () => { };
//
executor(resolve, reject);
}
}
基本状態の解決
秘籍はPromiseに規定されています.
new Promise((resolve, reject)=>{resolve(value)})
resolveは成功し、パラメータvalueを受信し、状態をfulfilledに変更し、再び変更することはできません.new Promise((resolve, reject)=>{reject(reason)})
rejectが失敗し、受信パラメータreason、状態がrejectedに変更され、再変更できません.そこで、以下のコードを入手しました.
class Promise{
constructor(executor){
// state
this.state = 'pending';
//
this.value = undefined;
//
this.reason = undefined;
let resolve = value => {
// state ,resolve
if (this.state === 'pending') {
// resolve ,state
this.state = 'fulfilled';
//
this.value = value;
}
};
let reject = reason => {
// state ,reject
if (this.state === 'pending') {
// reject ,state
this.state = 'rejected';
//
this.reason = reason;
}
};
// executor , reject
try{
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
}
thenメソッド
秘籍規定:Promiseにはthenという方法があり、中には2つのパラメータがあります:onFulfilled,onRejected,成功には成功の値があり、失敗には失敗の原因があります
class Promise{
constructor(executor){...}
// then onFulfilled onRejected
then(onFulfilled,onRejected) {
// fulfilled, onFulfilled,
if (this.state === 'fulfilled') {
onFulfilled(this.value);
};
// rejected, onRejected,
if (this.state === 'rejected') {
onRejected(this.reason);
};
}
}
これで武学が完成し、江湖の雑毛に対処できるようになったが、settimeout付きの江洋大盗には仕方がない.
非同期実装の解決
簡単な同期コードは基本的に実現できますが、resolveがsetTomeout内で実行され、thenのstateまたはpending待機状態が必要です.then呼び出し時に、成功と失敗をそれぞれの配列に保存し、rejectまたはresolveを呼び出すと、それらを呼び出す必要があります.
サブスクリプションのパブリッシュと同様に、then内の2つの関数を先に格納します.1つのpromiseに複数のthenがあるため、同じ配列内に存在します.
// then
let p = new Promise();
p.then();
p.then();
成功または失敗した場合、forEachはそれらを呼び出します.
class Promise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
//
this.onResolvedCallbacks = [];
//
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// resolve ,
this.onResolvedCallbacks.forEach(fn=>fn());
}
};
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
// reject ,
this.onRejectedCallbacks.forEach(fn=>fn());
}
};
try{
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled,onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value);
};
if (this.state === 'rejected') {
onRejected(this.reason);
};
// state pending
if (this.state === 'pending') {
// onFulfilled
this.onResolvedCallbacks.push(()=>{
onFulfilled(this.value);
})
// onRejected
this.onRejectedCallbacks.push(()=>{
onRejected(this.reason);
})
}
}
}
チェーンコールの解決
私のドアはよく
new Promise().then().then()
に使われています.これがチェーン呼び出しで、コールバック地獄を解決します.Promise中継状態は変更できないため、チェーン呼び出しを実現するにはthenメソッドの実行後の戻り値が新しいインスタンスである必要があります.
promiseのチェーン呼び出し(2つのケースに分けて議論)
promiseのthenメソッドはその後もpromiseオブジェクトを返します
例は次のとおりです.
let test = new Promise((resolve, reject) => {
let random = Math.random()
if (random > 0.5) {
resolve(' 0.5')
} else {
reject(' 0.5')
}
})
let p = test.then((result) => {
console.log(result)
return result
}).catch((result) => {
console.log(result)
return result
}).then((result) => {
console.log(result)
return result
}).then((result) => {
console.log('last', result)
})
console.log(p)
nodeを使用して実行した結果は次のとおりです.
p: Promise { <pending> }
0.5
0.5
last 0.5
結果から、thenメソッドを使用するとpromiseオブジェクトが返され、thenメソッドで呼び出し続けることができ、取得したパラメータが前のthenメソッドreturnの内容であることがわかります.
上記の例は、1回目のthen以降に返されるpromiseではrejectの状態は現れないが、1回目のthen以降にrejectの状態を含む可能性のあるpromiseを手動で返すと、エラーチェーン呼び出し中のエラー処理が大きな問題になる.2回目のthenはエラー処理メカニズムを持たないため、これはpromiseのreject状態に対応するエラー処理メカニズムを直接書くことを要求する.
Promiseのresove,reject,all,raceメソッド実装
//resolve
Promise.resolve = function(val){
return new Promise((resolve,reject)=>{
resolve(val)
});
}
//reject
Promise.reject = function(val){
return new Promise((resolve,reject)=>{
reject(val)
});
}
//race
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(resolve,reject)
};
})
}
//all ( promise, then, , )
Promise.all = function(promises){
let arr = [];
let i = 0;
function processData(index,data){
arr[index] = data;
i++;
if(i == promises.length){
resolve(arr);
};
};
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(data=>{
processData(i,data);
},reject);
};
});
}
後ろの部分はまだはっきり理解していません(続き..)
1、チェーン式を達成するために、最初のthenでpromiseをデフォルトで返します.秘籍はthenの中で新しいpromiseを返す方法を規定しています.promise 2:
promise2 = new Promise((resolve, reject)=>{})
と呼ばれています.2、私たちが最初のthenの中で
return
のパラメータ(パラメータが未知で、判断する必要があります).このreturnから出てきた新しいpromiseはonFulfilled()またはonRejected()の値です秘籍ではonFulfilled()またはonRejected()の値、すなわち最初のthenが返す値をxと規定し、xを判断する関数をresolvePromiseと呼ぶ
return
のオブジェクト)、resolve、reject class Promise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onResolvedCallbacks.forEach(fn=>fn());
}
};
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn=>fn());
}
};
try{
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled,onRejected) {
// promise2
let promise2 = new Promise((resolve, reject)=>{
if (this.state === 'fulfilled') {
let x = onFulfilled(this.value);
// resolvePromise , return promise promise2
resolvePromise(promise2, x, resolve, reject);
};
if (this.state === 'rejected') {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
};
if (this.state === 'pending') {
this.onResolvedCallbacks.push(()=>{
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
})
this.onRejectedCallbacks.push(()=>{
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
})
}
});
// promise,
return promise2;
}
}
resolvePromise関数の完了
秘籍はコードを規定して、異なるpromiseコードを互いに組み合わせて、resolvePromiseと言います
let p = new Promise(resolve => {
resolve(0);
});
var p2 = p.then(data => {
// , ,
return p2;
})
1、判断x
let then = x.then
xはオブジェクトまたは関数(デフォルトpromise) function resolvePromise(promise2, x, resolve, reject){
//
if(x === promise2){
// reject
return reject(new TypeError('Chaining cycle detected for promise'));
}
//
let called;
// x null x
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
// A+ , then = x then
let then = x.then;
// then , promise
if (typeof then === 'function') {
// then this
then.call(x, y => {
//
if (called) return;
called = true;
// resolve promise
resolvePromise(promise2, y, resolve, reject);
}, err => {
//
if (called) return;
called = true;
reject(err);//
})
} else {
resolve(x); //
}
} catch (e) {
//
if (called) return;
called = true;
// then
reject(e);
}
} else {
resolve(x);
}
}
その他の問題の解決
1、秘籍規定onFulfilled、onRejectedはすべてオプションのパラメータで、もし彼らが関数でなければ、無視しなければならない.
value => value
reason => throw err
、秘籍規定onFulfilledまたはonRejectedは同期して呼び出されず、非同期で呼び出さなければなりません.settimeoutを用いて非同期問題 class Promise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onResolvedCallbacks.forEach(fn=>fn());
}
};
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn=>fn());
}
};
try{
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled,onRejected) {
// onFulfilled , onFulfilled, value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// onRejected , onRejected,
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
let promise2 = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
//
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
if (this.state === 'rejected') {
//
setTimeout(() => {
//
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
//
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
//
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
};
});
// promise,
return promise2;
}
}
大功を成し遂げる
ついでにcatchとresolve、reject、race、allメソッドを添付します
class Promise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onResolvedCallbacks.forEach(fn=>fn());
}
};
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn=>fn());
}
};
try{
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled,onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
let promise2 = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
if (this.state === 'rejected') {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
};
});
return promise2;
}
catch(fn){
return this.then(null,fn);
}
}
function resolvePromise(promise2, x, resolve, reject){
if(x === promise2){
return reject(new TypeError('Chaining cycle detected for promise'));
}
let called;
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, y => {
if(called)return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, err => {
if(called)return;
called = true;
reject(err);
})
} else {
resolve(x);
}
} catch (e) {
if(called)return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
//resolve
Promise.resolve = function(val){
return new Promise((resolve,reject)=>{
resolve(val)
});
}
//reject
Promise.reject = function(val){
return new Promise((resolve,reject)=>{
reject(val)
});
}
//race
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(resolve,reject)
};
})
}
//all ( promise, then, , )
Promise.all = function(promises){
let arr = [];
let i = 0;
function processData(index,data){
arr[index] = data;
i++;
if(i == promises.length){
resolve(arr);
};
};
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(data=>{
processData(i,data);
},reject);
};
});
}
私たちのpromiseが正しいかどうかを検証する方法
1、先に下記のコードを付けます
2、npmにはpromises-aplus-testsプラグインnpm i promises-aplus-tests-gがあり、macユーザーの最前線にsudoを加えることができる.
3、コマンドラインpromises-aplus-tests[jsファイル名]で検証できる
//
//
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
module.exports = Promise;
//npm install promises-aplus-tests promise promisesA+