非同期処理ロジック@JavaScript¶
はじめに¶
本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。
01. 非同期処理¶
非同期処理¶
▼ 非同期処理とは¶
▼ 非同期処理化¶
処理の実行を部分的に遅らせると、後続する処理が先に実行される。
これは非同期処理である。
*実装例*
非同期化する処理をsetTimeout
関数に渡し、処理を遅らせる。
function asyncMethod() {
// 1秒だけ実行を遅らせる
setTimeout(function () {
console.log("foo");
}, 1000);
console.log("bar");
}
asyncMethod();
// 先にbarが実行される
// bar
// foo
Promiseオブジェクト¶
▼ Promiseオブジェクトとは¶
JavaScriptで、非同期処理の成否を管理し、後続する処理を定義できるオブジェクトのこと。
Promiseオブジェクトの実装の仕様は取り決められており、以下のリンクを参考にせよ。
▼ Promiseオブジェクトの種類¶
ネイティブなJavaScriptのPromiseオブジェクト、JQueryのPromiseオブジェクト、がある。
ネイティブの方が、Promiseオブジェクトの仕様により則った機能を持つ。
リリース日 | 提供 | 種類 | 説明 | 補足 |
---|---|---|---|---|
2012 | JQueryパッケージのDeferredモジュール | Promiseオブジェクト | バージョン1.5でPromiseオブジェクトが導入された。 ・https://api.jquery.com/category/version/1.5/ |
・https://api.jquery.com/category/deferred-object/ |
2015 | ビルトインオブジェクト | Promiseオブジェクト | JQueryのPromiseオブジェクトを参考にして、ES2015から新しく使用できるようになった。 | ・https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise |
2017 | ビルトインオブジェクト | async/await宣言 | ES2017から新しく使用できるようになった。ビルトインオブジェクトのPromiseオブジェクトをより使用しやすくしたもの。 | ・https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function |
02. ネイティブなJavaScript¶
Promiseオブジェクト¶
Promiseオブジェクトのコンストラクタに、非同期処理を持つ関数を渡すことにより、Promiseオブジェクトはこの関数内の非同期処理の成否を管理する。
const asyncFunc = () => {
return new Promise(
// 非同期処理を持つ関数を渡す
(resolve, reject) => {
// 関数内の非同期処理の成否が管理される
},
);
};
resolve
メソッド、reject
メソッド¶
▼ コンストラクタを使用する場合¶
Promiseオブジェクトのコンストラクタ内では、暗黙的にtry-catch
が実行されている。
そのため、結果のステータスが成功であればresolve
メソッドの結果を返却し、反対に失敗であればreject
メソッドを返却する。
両方を実装すると良しなに実行してくれる。
resolve
メソッドとresolve
メソッドのコール時にreturn
を使用しないと、後続する処理も実行される。
1つ目の書き方として、Promiseインスタンスのコールバック関数に渡す方法がある。
const asyncFunc = () => {
return new Promise((resolve, reject) => {
// ステータスが成功の場合に選択される。
resolve("SUCCESS"); // Promise { "SUCCESS" }
// ステータスが失敗の場合に選択される。
reject("FAILED"); // Promise { "FAILED" }
console.log("test");
});
};
console.log(asyncFunc());
// 後続する処理も実行され、resolveメソッドの結果が返却される。
// test
// Promise { 'SUCCESS' }
一方で、resolve
メソッドとresolve
メソッドのコール時にreturn
を使用すると、後続する処理は実行されない。
const asyncFunc = () => {
return new Promise((resolve, reject) => {
return resolve("SUCCESS");
reject("FAILED");
console.log("test");
});
};
console.log(asyncFunc());
// 後続する処理も実行されない。
// Promise { 'SUCCESS' }
▼ コンストラクタを使用しない場合¶
別の書き方として、Promiseオブジェクトから直接的にresolve
メソッドやreject
メソッドをコールしても良い。
この場合、必ずreturn
で返却する必要がある。
return
を使用しないと、何も返却されない。
const asyncFunc = () => {
// ステータスが成功の場合に選択される。
return Promise.resolve("SUCCESS"); // Promise { "SUCCESS" }
};
const asyncFunc = () => {
// ステータスが失敗の場合に選択される。
return Promise.reject("FAILED"); // Promise { "FAILED" }
};
console.log(asyncFunc()); // Promise { 'SUCCESS' }
const asyncFunc = () => {
return Promise.resolve("SUCCESS");
};
asyncFunc()
// 失敗時に返却されたrejectをハンドリング
.catch((reject) => {
// rejectメソッドを実行
reject;
})
.then((resolve) => {
// resolveメソッドを実行
resolve;
});
console.log(asyncFunc()); // Promise { 'SUCCESS' }
非同期処理内で両方をコールするとエラーになってしまう。
const asyncFunc = () => {
Promise.resolve("SUCCESS");
Promise.reject("FAILED");
};
console.log(asyncFunc()); // エラーになってしまう
UnhandledPromiseRejectionWarning: FAILED
(Use `node --trace-warnings ...` to show where the warning was created)
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
[DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
補足として、NodeのHTTPパッケージの関数は、Promiseインスタンスのコールバック関数として使用しないと、正しく挙動しない。
then
メソッド、catch
メソッド、finally
メソッド¶
▼ コンストラクタを使用する場合¶
*実装例*
const resolveFunc = new Promise((resolve, reject) => {
return resolve("resolve!!");
});
resolveFunc.then((value) => {
// resolveFuncがPromiseを返し、resolve!!がresolveされるため
// thenメソッドが実行されコンソールにresolve!!が表示される
console.log(value); // resolve!!
});
const resolveFunc = () => {
// resolveFuncはasync functionではないため、Promiseを返さない
return "resolve!!";
};
resolveFunc.then((value) => {
// resolveFuncはPromiseを返さないため、エラーが発生して動かない
// Uncaught TypeError: resolveError(...).then is not a function
console.log(value);
});
const rejectFunc = new Promise((resolve, reject) => {
reject(new Error("reject!!"));
});
rejectFunc.catch((err) => {
// rejectFuncがPromiseを返し、reject!!がrejectされるため
// catchメソッドが実行されコンソールにreject!!が表示される
console.log(err); // reject!!
});
02-02. async/await宣言¶
async宣言¶
▼ async宣言とは¶
Promiseオブジェクトを明示的に使用する場合、Promiseオブジェクトのコンストラクタに非同期処理を持つ関数を渡す必要がある。
一方で、async宣言された関数内の非同期処理は、Promiseオブジェクトに渡すための関数内に暗黙的に定義される。
Promiseや、これのコントラクタに渡す関数を実装する必要が無いため、可読性が高まる。
また、仮にPromiseオブジェクトをコールし、PromiseオブジェクトがPromiseオブジェクトに渡されてしまっても、結果的に入れ子にならないようによしなに処理してくれる。
*実装例*
以下の全ては、同じ処理を定義している。
const asyncFunc = async () => {
// Promiseオブジェクトに渡すための関数内に暗黙的に定義される。
return "SUCCESS";
};
// 単にreturnとしてもPromiseオブジェクトが返却される。
console.log(asyncFunc()); // Promise { "SUCCESS" }
const asyncFunc = async () => {
return new Promise((resolve, reject) => {
return resolve("SUCCESS"); // Promise { "SUCCESS" }
});
};
// Promiseオブジェクトを返却するようにしても、入れ子にはならない。
console.log(asyncFunc()); // Promise { "SUCCESS" }
const asyncFunc = async () => {
return Promise.resolve("SUCCESS"); // Promise { "SUCCESS" }
};
// Promiseオブジェクトを返却するようにしても、入れ子にはならない。
console.log(asyncFunc()); // Promise { "SUCCESS" }
また、axiosオブジェクトのようにPromiseオブジェクトをデフォルトで返却するメソッドを使用しても良い。
*実装例*
非道処理としてGETでリクエストを送信している。
// axiosオブジェクトのメソッドはPromiseオブジェクトを返却する。
const asyncFunc = async () => {
axios.get("/some/path").then((res) => {
console.log(res.data); // "some data"
});
};
await宣言とは¶
▼ await宣言¶
以降の全処理をthen
メソッドに渡す。
Promiseオブジェクトのthen
メソッドに相当するが、then
メソッドのようにメソッドチェーンする必要はなくなるため、可読性が高い。
時間のかかる非同期処理でこれを宣言すると、予期せず処理が流れてしまうことを防げる。
*実装例*
// Promiseオブジェクトのthenメソッドを使用した場合
const asyncFunc = async () => {
axios.get("/some/path").then((res) => {
console.log(res.data); // "some data"
});
};
// awaitを使用した場合
const asyncFunc = async () => {
// 以降の全処理がthenメソッドに渡される。
const res = await axios.get("/some/path");
console.log(res.data); // "some data"
};
await宣言により、コールバック地獄のコードが分かりやすくなる。
// Promiseオブジェクトのthenメソッドを使用した場合
const asyncFunc = async () => {
// コールバック関数地獄になっている。
axios.get("/some/path1").then((res) => {
const res1 = res;
axios.get("/some/path1").then((res) => {
const res2 = res;
console.log(res1.data + res2.data); // "some data"
});
});
};
// awaitを使用した場合
const asyncFunc = async () => {
const res1 = await axios.get("/some/path1");
const res2 = await axios.get("/some/path2");
console.log(res1.data + res2.data); // "some data"
};
エラーハンドリング¶
▼ try-catch¶
Promiseオブジェクトのthen
メソッド、catch
メソッド、finally
メソッドを使用してエラーハンドリングを実装できるが、try-catch文とawait宣言を組み合わせて、より可読性高く実装できる。
*実装例*
const asyncFunc = async () => {
return axios
.get("/some/path1")
.catch((error) => {
console.error(error);
})
.then((data) => {
console.info(data);
});
};
const asyncFunc = async () => {
// 初期化
let response;
try {
response = await axios.get("/some/path1");
console.info(response);
} catch (error) {
console.error(error);
}
return response;
};
スリープ¶
▼ setTimeout¶
指定した秒数だけ処理を待機する。
// 5秒待機する。
await new Promise((resolve) => {
setTimeout(resolve, 5000);
});
03. JQuery¶
Promiseオブジェクト¶
▼ done
メソッド、fail
メソッド、always
メソッド¶
Promiseオブジェクトが持つメソッド。
ajax
メソッドによってレスポンスを受信した後、その結果をdone
、fail
、always
の3
個に分類し、これに応じたコールバック処理を実行する。
*実装例*
JQueryパッケージのget
メソッドやpost
メソッドを使用した場合。
const url = "https://www.google.co.jp/";
$.get(url)
.done((data) => {
console.log(data);
})
.fail((error) => {
console.log(error);
});
const url = "https://www.google.co.jp/";
const params = {
name: "Hiroki",
};
$.post(url, params)
.done((data) => {
console.log(data);
})
.fail((error) => {
console.log(error);
});
JQueryパッケージのajax
メソッドを使用した場合。
const id = 1;
$.ajax({
type: "POST",
url: "/xxx/xxx/" + id + "/",
contentType: "application/json",
data: {
param1: "AAA",
param2: "BBB",
},
})
// 非同期通信の成功時のコールバック処理
.done((data) => {
console.log(data);
})
// 非同期通信の失敗時のコールバック処理
.fail((error) => {
console.log(data);
toastr.error("", "エラーが発生しました。");
})
// 非同期通信の成功失敗に関わらず常に実行する処理
.always((data) => {
this.isLoaded = false;
});
▼ then
メソッド¶
Promiseオブジェクトが持つメソッド。
ajax
メソッドによってレスポンスを受信した後、その結果をthen
メソッドの引数の順番で分類し、これに応じたコールバック処理を実行する。
非同期処理の後に同期処理を行いたい場合に使用する。
*実装例*
JQueryパッケージのajax
メソッドを使用した場合。
const id = 1;
$.ajax({
type: "POST",
url: "/xxx/xxx/" + id + "/",
contentType: "application/json",
data: {
param1: "AAA",
param2: "BBB",
},
})
// 最初のthen
.then(
// 引数1つめは通信成功時のコールバック処理
(data) => {},
// 引数2つめは通信失敗時のコールバック処理
(data) => {},
)
// 次のthen
.then(
// 引数1つめは通信成功時のコールバック処理
(data) => {},
);