プロミスソース実装について、あまりにも多くの答えがオンラインにありますが、私はまた、多くの情報を読んで、非常に明確ではありません。ある日私は学校の仕上げまで、関数型プログラミングのファンクタ概念を、約束の魚は、ソースコードのより深い理解を持っています。今日、私たちはどのような約束を再学習しましょう。
我々は、それが非同期のネストされたコールバックを解決するための連鎖方法と同期され、約束の誕生は、「コールバック地獄」の問題を解決するためのものであることを知っています。
何?同期チェーン?それは、我々はそれの研究にファンクタを考えたものではないですか?あなたはファンクタを理解していれば、その後、より簡単に理解するために約束のソースコードを学ぶために来ます。次に、我々は関係のようなものを持っているものファンクタと約束探ります。
シンプルなファンクタを実装することを約束
まずリコール数子のFunctorチェーンコール:
class Functor{
constructor (value) {
this.value = value ;
}
map (fn) {
return Functor.of(fn(this.value))
}
}
Functor.of = function (val) {
return new Functor(val);
}
Functor.of(100).map(add1).map(add1).map(minus10)
// var a = Functor.of(100);
// var b = a.map(add1);
// var c = b.map(add1);
// var d = c.map(minus10);
コアファンクタはこれです:すべてのファンクタファンクタが新しいオブジェクトであるオブジェクトのプロトタイプチェーン上のマップ機能を持ちます。FN新しいサブ関数を生成するために得られた値と、処理データファンクタに格納された地図に移動関数によって渡さ。
ETCが...ファンクタは、チェーンを同期化され、そして約束は非同期鎖です。この値は、その後、我々はthis.value値を渡す方法については、非同期的に上回っていますか?
function executor(resolve){
setTimeout(()=>{ resolve(100) },500)
}
我々は、データを取得するsetTimeout500 100ミリ秒によるシミュレーションの世話します。実際には、非常に単純な、私たちは、このデータを処理するコールバック関数の解決に渡すことができます。
class Functor {
constructor (executor) {
let self = this;
this.value = undefined;
// 回调函数,用来赋值给 value
function resolve(value){
self.value = value;
}
executor(resolve)
}
}
var a = new Functor(executor);
上記のコードを説明します。我々はすぐにコールバック関数を解決し、着信エグゼキュータと実行されます、我々は値の値を取得することができ、我々は、コールバック関数が値割り当てthis.valueの値を解決します定義します。
だから我々は、オブジェクトの完全な割り当てを緩和しました。それは得るために非同期であるため、我々はそれがメソッドを使用し、データをどのように扱いますか?
アイデアファンクタによると、データを収集した後、我々は、FN機能のマップは、着信データを処理するためにさせてください。それは非同期処理であるため、実行後にデータを取得するために解決するので、私たちは、コールバック関数、内部で実行するコールバックは、fnを定義します。最後に、次のFNファンクタを解決するために、処理の結果を保存します。
class Functor {
constructor (executor) {
let self = this;
this.value = undefined;
this.callback = null;
// 回调函数,用来赋值给 value
function resolve(value){
self.value = value
self.callback() // 得到 value 之后,在 callback 里面执行 map 传入的 fn 函数处理数据
}
executor(resolve)
}
map (fn) {
let self = this;
return new Functor((resolve) => {
self.callback = function(){
let data = fn(self.value)
resolve(data)
}
})
}
}
new Functor(executor).map(add1).map(add1)
同時に、同じファンクタの約束を呼び出します
チェーンコールに加えを約束するだけでなく、同時に通話で、例えば:
var a = new Functor(executor);
var b = a.map(add);
var c = a.map(minus);
ファンクタを呼び出すと同時に、上記のように。あなたはそれが実際にはCで実装されていることがわかります。その理由は単純であり、Bは、割り当てのコールバックを与え、次いでCは、コールバックの割り当てを与えました。だから、問題を上書きするbが実行されません。この問題は非常に簡単です解決するために、私たちは解決配列にコールバックをしましょう。
class Functor {
constructor (executor) {
let self = this;
this.value = undefined;
this.callbacks = [];
function resolve(value){
self.value = value;
self.callbacks.forEach(item => item())
}
executor(resolve)
}
then (fn) {
return new MyPromise((resolve) => {
this.callbacks.push (()=>{
let data = fn(this.value)
console.log(data)
resolve(data)
})
})
}
}
var a = new MyPromise(executor);
var b = a.then(add).then(minus);
var c = a.then(minus);
私たちは、コールバックの配列、メソッドごとにコールを定義します。コールバックは、アレイに保存されます。
コールバック関数は、解決の値を取得する場合は、各関数の実行中に横断。
コールバックが空の場合、forEachのも前に間違った問題を解決した、実行されません
し、我々はさらに名前ファンクタがMyPromiseで変更、マップはその後、変更され
、この=自己ましょう、のリターンを簡素化
増加は、コールバック関数を拒否します
我々は、すべての非同期呼び出しは、私たちは多くの場合、データを取得できない場合、エラーメッセージを返すことを知っています。このセクションでは、我々は、エラーを処理します。
class MyPromise {
constructor (executor) {
let self = this;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
function resolve(value){
self.value = value;
self.onResolvedCallbacks.forEach(item => item())
}
function reject(reason){
self.reason = reason;
self.onRejectedCallbacks.forEach(item => item());
}
executor(resolve, reject);
}
then (fn,fn2) {
return new MyPromise((resolve,reject) => {
this.onResolvedCallbacks.push (()=>{
let data = fn(this.value)
console.log(data)
resolve(data)
})
this.onRejectedCallbacks.push (()=>{
let reason = fn2(this.reason)
console.log(reason)
reject(reason)
})
})
}
}
実際には、非常に単純な、我々は、マルチエグゼキュータに通過されることリジェクト
決意を実行決定するために、非同期実行の結果を、または拒否
した後、我々が定義しMyPromiseと同様の方法を拒否するように解決
し、我々は、2つに渡す必要があるときパラメータ、FN、FN2
この時間は、ファイルを読み込む機能エグゼキュータ機能パッケージasyncReadFile非同期になります
function asyncReadFile(url){
return new MyPromise((resolve,reject) => {
fs.readFile(url, (err, data) => {
if(err){
console.log(err)
reject(err)
}else {
resolve(data)
}
})
})
}
var a = asyncReadFile('./data.txt');
a.then(add,mismanage).then(minus,mismanage);
これは、我々は通常約束非同期関数、見てそこに感じなかったプロセスをパッケージ化するプロセスです。我々は先に言及していないことをasyncReadFileをカリー化、十分に確認してください。
プロミス増加状態
私たちは、進捗状況が保留されている定義
正常に実行が満たされた後に
拒否に失敗します
class MyPromise {
constructor (executor) {
let self = this;
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
function resolve(value){
if (self.status === 'pending') {
self.status = 'fulfilled';
self.value = value;
self.onResolvedCallbacks.forEach(item => item())
}
}
function reject(reason){
if (self.status === 'pending') {
self.status = 'rejected';
self.reason = reason;
self.onRejectedCallbacks.forEach(item => item());
}
}
executor(resolve, reject);
}
then (fn,fn2) {
return new MyPromise((resolve,reject) => {
if(this.status === 'pending'){
this.onResolvedCallbacks.push (()=>{
let data = fn(this.value)
console.log(data)
resolve(data)
})
this.onRejectedCallbacks.push (()=>{
let reason = fn2(this.reason)
console.log(reason)
reject(reason)
})
}
if(this.status === 'fulfilled'){
let x = fn(this.value)
resolve(x)
}
if(this.status === 'rejected'){
let x = fn2(this.value)
reject(x)
}
})
}
}
var a = asyncReadFile('./data.txt');
a.then(add,mismanage).then(add,mismanage).then(add,mismanage);
今の方法は、fn(this.value)に渡すために最後に、我々はそれをフィルタリングするために説教たぶん数子を費やす必要があります。
たぶん数子の最適化
then (onResolved,onRejected) {
onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}
onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}
return new MyPromise((resolve,reject) => {
if(this.status === 'pending'){
this.onResolvedCallbacks.push (()=>{
let x = onResolved(this.value)
resolve(x)
})
this.onRejectedCallbacks.push (()=>{
let x = onRejected(this.reason)
reject(x)
})
}
if(this.status === 'fulfilled'){
let x = onResolved(this.value)
resolve(x)
}
if(this.status === 'rejected'){
let x = onRejected(this.value)
reject(x)
}
})
}
たぶんファンクタは、onResolved非常に単純であり、いくつかのフィルタリングを行ってonRejected。
概要
約束を理解することは非常に困難な概念であるが、核となるアイデアは常にファンクタです。
一方、基礎ファンクタにいくつかの非同期の実装を追加しました。非同期実装が脳細胞のよりコストの点で、太字は是非、考え方についてもっと考えるように時間がかかります!
参考リンク:幻想の関数型プログラミングの約束漂流