非同期プログラミング:コールバック地獄とPromise


非同期処理とは?
各勘定科目のJavaScriptの非同期処理は、特定のコードの演算が終了するまで、コードの実行を停止するのではなく、まず次のコードのJavaScriptのプロパティを実行することを意味します.
console.log("바로 실행됩니다!");

setTimeout(() => {
	console.log("2초 뒤에 실행됩니다!");
}, 2000);

console.log("제일 나중에 실행됩니다!");
¥2,000実際の運行結果は:
「直接運転開始!
「最後に実行します!」
「2秒後に運転開始!
非同期Web APIであるため、このメソッドは最も遅く実行されます.
このように、特定の論理実行の終了を待たずに、まず残りのコードを実行することが非同期処理である.
なぜ非同期処理が必要なのですか?
1朕は、すべてのことを順次処理する必要がある同期処理について、サーバからデータを受信するコードを最前端に置くとしたら、どうする?
2データ量が大きい場合は?
無限に待つ...
コールバック関数
搦搦コールバック関数は,特定の関数のパラメータとして伝達される関数である.
const testCallback = function (callback) {
  callback();
};

function callback() {
	console.log("this is callback")
}
 
testCallback(callback);
これにより,関数のパラメータで関数を伝達することができ,このとき伝達される関数をコールバック関数と呼ぶ.
コールバック関数を使用する理由
板橋隊長の例を見てみましょう.
次の例はajax通信を用いてサーバからデータを受信するコードである.
私たちは$です.get()メソッドでhttps://domain.com/から1番商品の情報を取得した後、この情報をtableData変数に割り当てたい.
function getData() {
    var tableData;
    $.get('https://domain.com/products/1', function (response) {
        tableData = response;
    });
    return tableData;
}
 
console.log(getData()); // undefined
でも….
$.get()メソッドは非同期メソッドなので、最後に実行され、tableDataにundefinedが割り当てられます.
(tableData 1は項目情報を含まない…)
コールバック関数を使用すると、予想される順序で処理できます.
function getData(callbackFunc) {
	$.get('https://domain.com/products/1', function(response) {
		callbackFunc(response); // 서버에서 받은 데이터 response를 callbackFunc() 함수에 넘겨줌
	});
}

getData(function(tableData) {
	console.log(tableData); // $.get()의 response 값이 tableData에 전달됨
});
非同期メソッド$get()実行後にデータを収集します.
コールバック関数を呼び出してコールバック関数のパラメータにデータを割り当てます.
私たちの最初の意図に従って、私たちはデータを受け取りました!
しかし、コールバック関数の使用には大きな問題があります!
以下はDREAM符号化Ellyのコールバック関数地獄の例である.
console.log(1);
setTimeout(function () {
  console.log(2);
}, 1000);
console.log(3);


// 동기 콜백
const printImmediately = function (print) {
  print();
};

printImmediately(() => { console.log('hello world!') });

// 비동기 콜백
const printWithDelay = function (print, delay) {
  setTimeout(print, delay);
}

printWithDelay(() => { console.log('hello world!') }, 2000);

// callback hell example
class UserStorage {
  loginUser(id, password, onSuccess, onError) {
    setTimeout(() => {
      if (
        (id === "ellie" && password === "dream") ||
        (id === "coder" && password === "academy")
      ) {
        onSuccess(id);
      } else {
        onError(new Error("not found!"));
      }
    }, 2000);
  }

  getRoles(user, onSuccess, onError) {
    setTimeout(() => {
      if (user === "ellie") {
        onSuccess( {name: "ellie", role: "admin"} );
      } else {
        onError(new Error("no Sucees!"));
      }
    }, 1000);
  }

  getPart(role, onSuccess, onError) {
    setTimeout(() => {
      if (role === "admin") {
        onSuccess ( {part: "develop"} );
      } else {
        onError(new Error("no Success!"));
      }
    }, 3000);
  }
}

const userStorage = new UserStorage();
const id = prompt("enter your id");
const password = prompt("enter your password");

userStorage.loginUser(
  id,
  password,
  (user) => {
    userStorage.getRoles(
      user,
      (userWithRoles) => {
        alert(`Hello! ${userWithRoles.name}, you have a ${userWithRoles.role} role!`);
        userStorage.getPart(
          userWithRoles.role,
          (userWithPart) => {
            alert(`Your part is ${userWithPart.part}!`);
          },
          (error) => {
            console.log(error);
          }
        )
      },
      (error) => {
        console.log(error);
      }
    );
  },
  (error) => {
    console.log(error);
  }
);
⑪callback関数の問題
コールバック関数地獄が起こったら...

  • 1▼読みづらい=>毒性が低い.
    (ビジネスロジックは一目瞭然ではありません)

  • 2πは,エラーやデバッグが必要な場合でも非常に困難である.
    △エラーがどこで発生したかを特定するのは難しい.

  • 3」メンテナンスも大変です.
  • 注意:javascript非同期処理の起動コールバックを理解し、コールバック地獄を体験する😱 JavaScript Calback|フロントエンド開発者入門(JavaScript ES 6)
    Promise
    'use strict';
    
    // Promise is JavaScript object for asynchronous operation.
    // State : panding -> fulfilled or rejected
    // Producer vs Consumer.
    
    // 1. Producer
    
    const id = "ellie";
    
    // doing some Heavy work (ex. network, read files)
    const promise = new Promise((resolve, reject) => {
    
      // when new Promise is created, the excutor runs automatically.
      // promise 를 만들자마자 기능을 수행해버림 (익스큐터라는 콜백함수가 바로 실행되버림).
      // 만약 사용자가 버튼을 누른 순간부터 기능을 수행하고 싶다면? => ??
      console.log('doing something!');
    
      if (id === "ellie") {
        setTimeout(() => {
          resolve('ellie');
        }, 2000);
      } else {
        setTimeout(() => {
          reject(new Error('no network!'));
        }, 3000)
      }
    
    });
    
    // 2. Consumer : then, catch, finally
    promise
      .then((value) => {
        console.log(value);
      })
      .catch(error => {
        console.log(error);
      })
      .finally(() => {
        console.log("end!");
      })
    
    // 3. Promise chaining.
    const fetchNumber = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(1);
      }, 1000);
    });
    
    // then은 값을 바로 전달할 수도 있고, promise를 전달할 수 도 있다.
    fetchNumber
      .then((num) => num * 2)
      .then((num) => num * 3)
      .then((num) => {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(num - 1);
          }, 1000);
        });
      })
      .then((num) => console.log(num));
    
    // 4. Error Handling.
    const getHen = () => {
      return new Promise((resolve, reject) => {
        setTimeout(() => { resolve('🐓') }, 1000);
      });
    };
    
    const getEgg = (hen) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => { reject(new Error(`${hen} => 🥚`)) }, 1000);
      });
    };
    
    const cook = (egg) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => { resolve(`${egg} => 🍳`) }, 1000);
      });
    };
    
    // getHen()
    //   .then(hen => getEgg(hen))
    //   .then(egg => cook(egg))
    //   .then(meal => console.log(meal));
    
    getHen() //
      .then(getEgg)
      .catch(error => {
        return '🥖';
      })
      .then(cook)
      .then(console.log);
    
    DREAM符号化EllyのPromise講義コード
    const id = prompt('enter your id');
    const password = prompt('enter your password');
    
    const takeUserId = () => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if (
            (id === 'ellie' && password === 'dream') ||
            (id === 'coder' && password === 'academy')
          ) {
            resolve(id);
          } else {
            reject(new Error('input correct "id" or "password"!'));
          }
        }, 1000);
      });
    };
    
    const takeUserRole = (userId) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if (userId === "ellie") {
            const userRole = "admin";
            resolve(userRole);
          } else if (userId === "coder") {
            const userRole = "play";
            resolve(userRole);
          } else {
            reject(new Error('Cant find user role!'));
          }
        }, 1000);
      });
    };
    
    const takeUserPart = (userRole) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if (userRole === "admin" || userRole === "play") {
            const userPart = 'development';
            resolve(console.log(`Your part is ${userPart}!`));
          } else {
            reject(new Error('dont find your role!'));
          }
        }, 1000);
      });
    };
    
    takeUserId() //
      .then(takeUserRole)
      .then(takeUserPart);
    
    プロミスを用いて,以前の例のコールバック地獄の様子を解決した.
    注意:JavaScript 12。JavaScript Promise|フロントエンド開発者入門(JavaScript ES 6)
    ソース:非同期プログラミング|JavaScriptワールド
    JS]非同期(async)プログラミングについて[1]。JS非同期モード紹介|by Kwoncheol Shin|Medium
    JavaScript非同期処理とコールバック関数•Capacityブリッジ
    JavaScript非同期タスクの処理方法
    1.同期と非同期、コールバック関数
    第2部-JavaScript非同期プログラミング(コールバック、インフラストラクチャ)