JavaScript Deep Dive-45章(Promis)

29927 ワード

プロセスの作成


new演算子を使用してpromiseコンストラクション関数を呼び出すと、promiseオブジェクトが生成されます.
ES 6で導入された承諾は、ホストオブジェクトではなくECMAScript仕様で定義された標準構築オブジェクトです.
Promiseコンストラクション関数は、非同期処理を実行するコールバック関数をパラメータとして渡し、このコールバック関数は解析および拒否関数をパラメータとして渡します.
const promise = new Promise((resolve,reject) => {
  //
  if (  ){
  resolve('result');
}else{ 
	reject('failure reason');
}
});
パラメータとしてPromiseコンストラクション関数が渡されるコールバック関数の内部で非同期処理を実行します.
非同期処理が成功すると、コールバック関数のパラメータとして渡される解析関数が呼び出され、非同期処理が失敗すると、拒否関数が呼び出されます.
前述した非同期関数getを使用して再実装します.
//GET 요청을 위한 비동기 함수 
const promiseGet = url => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET',url);
    xhr.send();
    
    xhr.onload = () => {
      if ( xhr.status === 200 ) {
        //성공적으로 응답을 전달받으면 resolve 함수를 호출한다.
        resolve(JSON.parse(xhr.response));
      } else {
        //에러 처리를 위해 reject 함수를 호출한다.
        reject(new Error(xhr.status));;
      }
    }
  });
};


//promiseGet 함수는 promise를 반환한다.
promiseGet('https://www.abc.def/post/1');
非同期関数promeseGetは、関数内部で生成され、promisを返します.
非同期処理はPromiseコンストラクション関数がパラメータとして伝達されるコールバック関数で実行される.
非同期処理が成功すると、非同期処理結果はパラメータとして解析関数に渡され、呼び出されます.非同期処理が失敗すると、エラーはパラメータとして拒否関数に渡されます.
パラメータとしてresolve関数に渡すときに呼び出されますか?
reselve((res) => console.log(res));
reject((err) => console.error(err));
res,errが非同期処理結果を含むことを示す.

プロセスステータス


生成後のプロセスは基本的に保留中です.
その後、非同期処理が実行されると、プロセスの状態は非同期処理の結果に応じて変化します.
  • 非同期処理成功:解析関数を呼び出してプロセスを完了状態
  • に変更
  • 非同期処理に失敗しました.拒否関数を呼び出してプロセスを拒否状態に変更します.
  • 完了または拒否されたステータスを固定ステータスと呼びます.
    固定状態とは、完了または拒否状態に関係なく、非保留状態で非同期処理を実行する状態をいう.
    プロセスは、「保留中」状態から「完了」または「却下済」状態、すなわち「解決済」状態に変更できます.
    しかし、いったん安定すると、もう別の状態にはならない.
    プロセスには非同期処理状態と非同期処理結果があります.
    非同期処理に失敗すると、プロセスは保留状態から拒否状態に変わります.
    そして、非同期処理結果Errorオブジェクトを値とする.
    すなわち,プロセスは非同期処理状態と処理結果を管理するオブジェクトである.

    Premis後続処理方法


    プロセスの非同期処理状態が変化した場合、対応する後続処理が必要です.
    たとえば、プロセスが完了している場合は、プロセスの処理結果を操作する必要があります.プロセスが拒否されている場合は、プロセスの処理結果をエラー処理する必要があります.
    このため、Promissは後続の方法thenとcatch finallyを提供します.
    プロセスの非同期処理状態が変化すると、パラメータとして後続の処理方法に渡されるコールバック関数が選択的に呼び出される.
    このとき,プロセスの処理結果はパラメータとして後続の処理方法のコールバック関数に渡される.
    後続の処理方法はすべて1つのプロセスを返し、非同期で実行します.
    Promisの次の処理方法は以下の通りです.

    Promise.prototype.then


    次に、メソッドは2つのコールバック関数をパラメータとして渡します.
  • 最初のコールバック関数は、プロセスの完了(解析関数が呼び出される)時に呼び出される.このときコールバック関数はpromisの非同期処理結果を引数として伝達する.
  • 第2のコールバック関数は、プロセスが拒否(拒否関数が呼び出される)されたときに呼び出される.このときコールバック関数はpromisに渡されるエラーをパラメータとします.
  • //fulfilled
    new Promise(resolve => resolve('fulfilled'))
    .then( v => console.log(v), e => console.log(e)); // fulfilled
    
    //rejected
    
    new Promise ((_, reject) => reject(new Error('rejected')))
    .then(v => console.log(v), e => console.error(e)); // Error: rejected
    そして方法はいつもプロミスに戻る.
    その後、メソッドのコールバック関数がプロセスを返すと、元のままになります.コールバック関数がプロセス以外の値を返すと、暗黙的な解析または拒否によってプロセスが生成され、返されます.
    Promis以外の値を返しますか?
    パラメータとして受信した結果を操作することなく、resolveとrejectを異なる値で区別することもできます.

    Promise.prototype.catch


    catchメソッドは、パラメータとしてコールバック関数を渡します.catchメソッドのコールバック関数は、プロセスが拒否されている場合にのみ呼び出されます.
    new Promise((_, reject) => reject(new Error('rejected')))
    .catch(e => console.log(e)); // Error : rejected
    catch法の挙動はthen(Undefined,onRejected)と同じである.
    したがってthenメソッドと同様にpromisは常に返される.
    new Promise((_, reject) => reject(new Error('rejected')))
    .then(undefined, e => console.log(e)); // Error: rejected

    Promise.prototype.finally


    finallyメソッドは、成功と失敗にかかわらず無条件にコールバック関数をパラメータとして渡します.finallyメソッドは,プロセスの状態にかかわらず,共同で実行する必要がある処理内容がある場合に非常に有用である.
    finallyメソッドはthen/catchメソッドと同様にpromisを常に返します.
    new Promise(() => {})
    .finally(() => console.log('finally')); // finally

    エラー処理

    
    const wrongURL = 'https://wqeq.qwe.rqw/2323/1';
    
    //부적절한 URL의 지정으로 에러 발생
    promiseGet(wrongUrl).then(
      res => console.log(res),
      err => console.error(err)
      ); // Error: 404
    非同期処理で発生したエラーは、プロセスの後続処理方法catchを用いて処理することもできる.
    promiseGet(wrongUrl)
    .then(res => console.log(res));
    .catch(err => console.error(err)); // Error: 404
    catchメソッドを呼び出すと、内部でthen(定義されていません、onRejected)が呼び出されます.
    したがって、上記の例は、以下のように内部処理される.
    
    promiseGet(wrongUrl)
    .then(res => console.log(res))
    .then(undefined, err => console.error(err)); // Error : 404
    
    しかし,then法の2回目のコールバック関数はεs回目のコールバック関数による誤りを捉えることができず,コードが複雑になり,可読性が悪い.
    すべてのthenメソッドを呼び出した後にcatchメソッドを呼び出すと、非同期処理で発生したエラーとthenメソッド内部で発生したエラーをキャプチャできます.
    さらに,catch法を用いると,方法に2回のコールバック関数を伝達するよりも可読性と明確性が高い.

    マイクロタスクキュー

    setTimeout(() => console.log(1), 0);
    
    promise.resolve()
    .then(() => console.log(2));
    .then(() => console.log(3));
    プロミスの後続の処理方法も非同期で、1 2 3の順に出力されるように見えますが、2 3 1の順に出力されます.
    これは、Promisの後続の処理方法のコールバック関数が、タスクキューではなくマイクロテクノロジーキューに格納されるためである.
    マイクロテクノロジーはタスクキューとは異なるキューです
    マイクロテクノロジーキューはPromisの後続処理方法のコールバック関数を一時的に格納する.他の非同期関数のコールバック関数またはイベントハンドラは、タスクキューに一時的に格納されます.
    コールバック関数またはイベントハンドラを一時的に格納するのはタスクキューと同じですが、マイクロタスクキューの優先度はタスクキューより高いです.すなわち、callスタックが空の場合、イベントループは、マイクロタスクキューで待機している関数を最初にインポートして実行します.その後、マイクロタスクキューが空の場合、タスクキューで待機している関数がインポートされ、実行されます.

    fetch


    fetch関数はXMLHttpRequestオブジェクトと同様にHTTPリクエスト転送機能を提供するクライアントWeb APIである.
    XMLHttpRequestオブジェクトよりも使いやすく、プロセスをサポートします.
    非同期処理に用いるコールバックモードの欠点から解放される.
    const promise = fetch(url,option)
    fetch関数は、HTTP応答を表す応答オブジェクトがパッケージされたPromiseオブジェクトを返します.
    fetch関数は、HTTP応答を表す応答オブジェクトをパッケージ化するプロセスを返すので、解析した応答オブジェクトを後続の処理方法で受信することができる.応答オブジェクトは、HTTP応答を表す様々なプロパティを提供する.
    たとえば、応答.prototype.JsonメソッドはJsonparseのように、応答体を逆シーケンス化します.

    Postリクエスト

    request.post('https://some/'), {
      userId: 1,
      title: 'Javascript',
      completed: false
    }).then(response => {
      if(!response.ok) throw new Error(response.statusText);
      return response.json();
    })
    .then(todos => console.log(todos))
    .catch(err => console.error(err));
    // { userId: 1. title: "Javascript". completed: false, id:201 }

    要求PATCH

    request.patch('https://some/'), {
      completed: true
    }).then(response => {
      if(!response.ok) throw new Error(response.statusText);
      return response.json();
    })
    .then(todos => console.log(todos))
    .catch(err => console.error(err));
    // { userId:1, id:1, title:"delectus aut autom", completed:true }

    DELETEリクエスト

    request.delete('https://some/')
    .then(response => {
      if(!response.ok) throw new Error(response.statusText);
      return response.json();
    })
    .then(todos => console.log(todos))
    .catch(err => console.error(err));
    // {}