Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

非同期処理(JavaScript)のプログラムを作成 #3

Merged
merged 33 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6318887
install sqlite3
sjabcdefin Jan 13, 2025
3081ef8
Create programs using node-sqlite3 in the form of callback
sjabcdefin Jan 15, 2025
0a558c9
Create programs using Promise wrapper for node-sqlite3 methods(run, all)
sjabcdefin Jan 21, 2025
c5fb47a
Create programs using async/await
sjabcdefin Jan 22, 2025
952081f
Rename files to use "error" instead of "err" and "no_error" instead o…
sjabcdefin Jan 27, 2025
2047128
Use double quotes instead of backquotes
sjabcdefin Jan 27, 2025
f1c8683
Refactor SQL queries by removing unnecessary line breaks and indentation
sjabcdefin Jan 27, 2025
77123aa
Removed abstraction in asynchronous processing and rewrote it in a di…
sjabcdefin Jan 29, 2025
9b90b88
Writes an empty array [] even if no parameters are specified
sjabcdefin Jan 29, 2025
95cd7d9
Fixed db.all to use arrow function instead of function expression
sjabcdefin Jan 29, 2025
b7d6d4d
Rename insertTitleQuery to insertBookRecordQuery
sjabcdefin Jan 30, 2025
526b0a3
Change log messages to passive voice
sjabcdefin Feb 1, 2025
45f478f
Correct spelling mistakes
sjabcdefin Feb 1, 2025
edf50f3
Rename variable name in record insertquery
sjabcdefin Feb 8, 2025
ef60c8e
Rename variable name in table dletion query
sjabcdefin Feb 8, 2025
26e8ba8
Remove empty array when parameters are not spericied
sjabcdefin Feb 8, 2025
7f8856a
Remove main function
sjabcdefin Feb 8, 2025
00979e0
Fixed to use arrow function instead of function expression
sjabcdefin Feb 8, 2025
68c7e42
Fixed to insert records serially instead of parallel
sjabcdefin Feb 8, 2025
233099e
Refactor db.close() to be an asynchronous function and move it to sql…
sjabcdefin Feb 8, 2025
56986a0
Remove the default value [] for the params argument in runQueryAsync
sjabcdefin Feb 8, 2025
f23f1de
Add params argument to db.all
sjabcdefin Feb 8, 2025
12973cf
Fixed to catch only the specified exception
sjabcdefin Feb 8, 2025
6ef5baf
Fixed to use arrow functions for db.run where this is not used
sjabcdefin Feb 12, 2025
d722ec9
Rename variable name in insertquery and selectquery
sjabcdefin Feb 12, 2025
9601d22
Rename closeQueryAsync to closeDatabaseAsync
sjabcdefin Feb 12, 2025
a57a47a
Fixed to use concise writing when arrow functions only contain return
sjabcdefin Feb 12, 2025
8ec4514
Refactor to be Top-level await
sjabcdefin Feb 12, 2025
92ebeb1
Move DB close processing to finally block
sjabcdefin Feb 12, 2025
eddc819
Handle specific SQLite errors and rethrow unexpected ones
sjabcdefin Feb 12, 2025
4d22ca9
Rename files from asyncawait to async_await
sjabcdefin Feb 13, 2025
1726f9d
Fixed to rethrow the original exception
sjabcdefin Feb 13, 2025
64325e2
Fixed to handle only expected errors
sjabcdefin Feb 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,378 changes: 1,359 additions & 19 deletions 03.asynchronous/package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion 03.asynchronous/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@
"globals": "^15.9.0",
"prettier": "^3.3.3"
},
"type": "module"
"type": "module",
"dependencies": {
"sqlite3": "^5.1.7"
}
}
49 changes: 49 additions & 0 deletions 03.asynchronous/sqlite_asyncawait_err.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env node

import sqlite3 from "sqlite3";
import { runQueryAsync, allQueryAsync } from "./sqlite_utils.js";

sqlite3.verbose();
const db = new sqlite3.Database(":memory:");

const createTableQuery = `
CREATE TABLE books (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL UNIQUE
)
`;
const insertTitleQuery = `INSERT INTO books (title) VALUES (?)`;
const selectAllQuery = `SELECT * FROM movies`;
const deleteTableQuery = `DROP TABLE books`;

const titles = ["I Am a Cat", "I Am a Cat", "SANSHIRO"];

async function main() {
await runQueryAsync(db, createTableQuery);
console.log("Table created");

for (const title of titles) {
try {
const result = await runQueryAsync(db, insertTitleQuery, [title]);
console.log(`Record inserted successfully with ID: ${result.lastID}`);
} catch (err) {
console.error(`Error occurred while inserting record: ${err.message}`);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

catch では全ての例外が無条件に捕捉されるので、この実装だと例外が握り潰されています。例外は握り潰さずに、捕捉したい例外のみを捕捉するようにしてください。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コミット:12973cf

err.codeの判定処理を追加し、捕捉したい例外のみを捕捉するように修正いたしました。
こちらのご指摘は、Promiseにも該当すると思い、Promiseも修正しております。🙏

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

例外が引き続き握り潰されています。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コミット:eddc819

想定しているエラー(SQLITE_CONSTRAINT、SQLITE_ERROR)以外についても握りつぶさず、捕捉するように修正いたしました。

}

try {
const rows = await allQueryAsync(db, selectAllQuery);
console.log("All records fetched successfully");
for (const row of rows) {
console.log(`id:${row.id}, title:${row.title}`);
}
} catch (err) {
console.error(`Error occurred while fetching records: ${err.message}`);
}

await runQueryAsync(db, deleteTableQuery);
console.log("Table deleted");
db.close();
}

main();
41 changes: 41 additions & 0 deletions 03.asynchronous/sqlite_asyncawait_noerr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env node

import sqlite3 from "sqlite3";
import { runQueryAsync, allQueryAsync } from "./sqlite_utils.js";

sqlite3.verbose();
const db = new sqlite3.Database(":memory:");

const createTableQuery = `
CREATE TABLE books (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL UNIQUE
)
`;
const insertTitleQuery = `INSERT INTO books (title) VALUES (?)`;
const selectAllQuery = `SELECT * FROM books`;
const deleteTableQuery = `DROP TABLE books`;

const titles = ["I Am a Cat", "KOKORO", "SANSHIRO"];

async function main() {
await runQueryAsync(db, createTableQuery);
console.log("Table created");

for (const title of titles) {
const result = await runQueryAsync(db, insertTitleQuery, [title]);
console.log(`Record inserted successfully with ID: ${result.lastID}`);
}

const rows = await allQueryAsync(db, selectAllQuery);
console.log("All records fetched successfully");
for (const row of rows) {
console.log(`id:${row.id}, title:${row.title}`);
}

await runQueryAsync(db, deleteTableQuery);
console.log("Table deleted");
db.close();
}

main();
73 changes: 73 additions & 0 deletions 03.asynchronous/sqlite_callback_err.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env node

import sqlite3 from "sqlite3";

sqlite3.verbose();
const db = new sqlite3.Database(":memory:");

const createTableQuery = `
CREATE TABLE books (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL UNIQUE
)
`;
const insertTitleQuery = `INSERT INTO books (title) VALUES (?)`;
const selectAllQuery = `SELECT * FROM movies`;
const deleteTableQuery = `DROP TABLE books`;

const titles = ["I Am a Cat", "I Am a Cat", "SANSHIRO"];
let insertCount = 0;

function main() {
createTable();
}

function createTable() {
db.run(createTableQuery, insertTitles);
}

function insertTitles() {
console.log("Table created");
for (const title of titles) {
db.run(insertTitleQuery, [title], displayID);
}
}

function displayID(err) {
if (err) {
console.error(`Error occurred while inserting record: ${err.message}`);
} else {
console.log(`Record inserted successfully with ID: ${this.lastID}`);
}
insertCount++;
if (insertCount === titles.length) {
fetchAll();
}
}

function fetchAll() {
db.all(selectAllQuery, displayAll);
}

function displayAll(err, rows) {
if (err) {
console.error(`Error occurred while fetching records: ${err.message}`);
} else {
console.log("All records fetched successfully");
for (const row of rows) {
console.log(`id:${row.id}, title:${row.title}`);
}
}
deleteTable();
}

function deleteTable() {
db.run(deleteTableQuery, closeDB);
}

function closeDB() {
console.log("Table deleted");
db.close();
}

main();
65 changes: 65 additions & 0 deletions 03.asynchronous/sqlite_callback_noerr.js

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noerr ではなく no err だと思います。それとファイル名に略称を使うのは分かりづらいのでやめましょう。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ファイル名について、noerr を no_error、err を error に修正いたしました。

コミット:952081f

Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env node

import sqlite3 from "sqlite3";

sqlite3.verbose();
const db = new sqlite3.Database(":memory:");

const createTableQuery = `
CREATE TABLE books (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL UNIQUE
)
`;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これだと改行分やインデント分も文字列に含まれるので、以下のような文字列になってしまいます。

<改行>
<空白><空白>CREATE TABLE books (
<空白><空白><空白><空白>id INTEGER PRIMARY KEY AUTOINCREMENT,
<空白><空白><空白><空白>title TEXT NOT NULL UNIQUE
<空白><空白>)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

特別長いSQL文ではないため、
可読性を上げるための余分な改行やインデントは不要と考え、一行で書くようにいたしました。
コミット:f1c8683

const insertTitleQuery = `INSERT INTO books (title) VALUES (?)`;
const selectAllQuery = `SELECT * FROM books`;
const deleteTableQuery = `DROP TABLE books`;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

文字列中に変数を埋め込んでいるわけではないのにテンプレート文字列が使われています。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

すみません。以前のプラクティスでも同様の指摘をいただいておりました。😭🙏
ダブルクウォーテーションを使用するように修正いたしました。

コミット:2047128


const titles = ["I Am a Cat", "KOKORO", "SANSHIRO"];
let insertCount = 0;

function main() {
createTable();
}

function createTable() {
db.run(createTableQuery, insertTitles);
}

function insertTitles() {
console.log("Table created");
for (const title of titles) {
db.run(insertTitleQuery, [title], displayID);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コールバックベースの非同期処理を for 文で制御することはできませんよ。

Copy link
Owner Author

@sjabcdefin sjabcdefin Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

すみません。
コールバックベースの非同期処理を制御できる繰り返し処理はない認識であっているでしょうか。💦🙇‍♀️
(複数の非同期処理の待ち合わせはコールバックベースではサポートされていないに該当する。)
レコード追加分、db.runを記述いたしました。

コミット:77123aa

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ないわけではないです。再起を用いればコールバックベースでも繰り返し処理を実現することはできます。ただし、node-sqlite3 が提供する関数だけでそれを実現することはできません。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご回答ありがとうございます。😌
かしこまりました。

}

function displayID() {
console.log(`Record inserted successfully with ID: ${this.lastID}`);
insertCount++;
if (insertCount === titles.length) {
fetchAll();
}
}

function fetchAll() {
db.all(selectAllQuery, displayAll);
}

function displayAll(err, rows) {
console.log("All records fetched successfully");
for (const row of rows) {
console.log(`id:${row.id}, title:${row.title}`);
}
deleteTable();
}

function deleteTable() {
db.run(deleteTableQuery, closeDB);
}

function closeDB() {
console.log("Table deleted");
db.close();
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

いずれのプログラムでも独自の抽象度の関数を定義するのは避けてください。非同期処理の表現方法が移り変わっていく様子をプログラム間で対応づけて理解してほしいのですが、それが実現できなくなってしまうためです。

Copy link
Owner Author

@sjabcdefin sjabcdefin Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

独自の抽象度の関数を削除し、非同期処理をそのまま記述するように修正いたしました。
コミット:77123aa

ひとまず、コールバックベースを修正しております。
そのため、Promiseやasync/awaitでご指摘いただいた以下の項目について、コールバックベースでも修正いたしました。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コミット:7f8856a

Promise、async/awaitについて、非同期処理の表現方法が移り変わっていく様子を分かりやすくするため、main関数を削除いたしました。


main();
63 changes: 63 additions & 0 deletions 03.asynchronous/sqlite_promise_err.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env node

import sqlite3 from "sqlite3";
import { runQueryAsync, allQueryAsync } from "./sqlite_utils.js";

sqlite3.verbose();
const db = new sqlite3.Database(":memory:");

const createTableQuery = `
CREATE TABLE books (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL UNIQUE
)
`;
const insertTitleQuery = `INSERT INTO books (title) VALUES (?)`;
const selectAllQuery = `SELECT * FROM movies`;
const deleteTableQuery = `DROP TABLE books`;

const titles = ["I Am a Cat", "I Am a Cat", "SANSHIRO"];

function main() {
return runQueryAsync(db, createTableQuery)
.then(function () {
console.log("Table created");
const requests = titles.map(function (title) {
return runQueryAsync(db, insertTitleQuery, [title])
.then(function (result) {
console.log(
`Record inserted successfully with ID: ${result.lastID}`,
);
})
.catch(function (err) {
console.error(
`Error occurred while inserting record: ${err.message}`,
);
});
});
return Promise.all(requests);
})
.then(function () {
return allQueryAsync(db, selectAllQuery);
})
.then(function (rows) {
console.log("All records fetched successfully");
for (const row of rows) {
console.log(`id:${row.id}, title:${row.title}`);
}
})
.catch(function (err) {
console.error(`Error occurred while fetching records: ${err.message}`);
})
.then(function () {
return runQueryAsync(db, deleteTableQuery);
})
.then(function () {
console.log("Table deleted");
})
.finally(function () {
db.close();
});
}

main();
54 changes: 54 additions & 0 deletions 03.asynchronous/sqlite_promise_noerr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env node

import sqlite3 from "sqlite3";
import { runQueryAsync, allQueryAsync } from "./sqlite_utils.js";

sqlite3.verbose();
const db = new sqlite3.Database(":memory:");

const createTableQuery = `
CREATE TABLE books (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL UNIQUE
)
`;
const insertTitleQuery = `INSERT INTO books (title) VALUES (?)`;
const selectAllQuery = `SELECT * FROM books`;
const deleteTableQuery = `DROP TABLE books`;

const titles = ["I Am a Cat", "KOKORO", "SANSHIRO"];

function main() {
return runQueryAsync(db, createTableQuery)
.then(function () {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

見づらいので使い分けが必要ない場合は function 式ではなくアロー関数を使ってください。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コミット:00979e0

アロー関数を使用するように修正いたしました。
なお、sqlite_utils.js の runQueryAsync関数のdb.runのみコールバック関数内でthisを使用しているため、function () { ... }式を使用しています。🙏🙇‍♀️
#3 (comment) でのやり取り結果で、修正が必要な場合は、修正いたします。🙇‍♀️

console.log("Table created");
const requests = titles.map(function (title) {
return runQueryAsync(db, insertTitleQuery, [title]).then(
function (result) {
console.log(
`Record inserted successfully with ID: ${result.lastID}`,
);
},
);
});
return Promise.all(requests);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

各処理は並列ではなく直列に実行してください。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コミット:68c7e42

並列ではなく、直列で実施するように修正いたしました。

})
.then(function () {
return allQueryAsync(db, selectAllQuery);
})
.then(function (rows) {
console.log("All records fetched successfully");
for (const row of rows) {
console.log(`id:${row.id}, title:${row.title}`);
}
return runQueryAsync(db, deleteTableQuery);
})
.then(function () {
console.log("Table deleted");
})
.finally(function () {
db.close();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

close もコールバックベースの非同期処理を行う関数なので Promise として扱えるようにしてください。

Copy link
Owner Author

@sjabcdefin sjabcdefin Feb 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コミット:233099e

close について Promise として扱えるように修正いたしました。

});
}

main();
23 changes: 23 additions & 0 deletions 03.asynchronous/sqlite_utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export function runQueryAsync(db, query, params = []) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

質問ですが、params にデフォルト引数を設定しているのは何のためですか?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

質問に対する回答がないです。

Copy link
Owner Author

@sjabcdefin sjabcdefin Feb 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

失礼いたしました。🙏🙇‍♀️
node-sqlite3 の API ドキュメント https://github.com/TryGhost/node-sqlite3/wiki/API
に、
以下の記載があったため、第2引数に[]を指定したほうがよいと思い、デフォルト引数として指定いたしました。
認識に誤りがありましたら、教えていただけると幸いです。

run(sql [, param, ...] [, callback])
...
In case you want to keep the callback as the 3rd parameter, you should set param to "[]" ( Empty Array ) as per TryGhost/node-sqlite3#116

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コミット:56986a0

以下の理由のため、[]を削除(省略)いたしました。
・#116 の問題は発生していない
・クエリに?がある場合は、paramsが必ず指定され、?がない場合、paramsはundefinedになるが、クエリの実行には問題ない。

return new Promise(function (resolve, reject) {
db.run(query, params, function (err) {
if (err) {
reject(err);
} else {
resolve(this);
}
});
});
}

export function allQueryAsync(db, query) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

元の all 関数もパラメータを受け付けるはずです。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コミット:f23f1de

allQueryAsync関数及びdb.allの引数に、paramsを追加いたしました。

return new Promise(function (resolve, reject) {
db.all(query, function (err, rows) {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
}