質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Electron

Electronは、HTML5とNode.jsというWebの技術を用いてデスクトップアプリケーションを作成できるクロスプラットフォームな実行環境です。

SQLite

SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Q&A

解決済

2回答

3973閲覧

javascriptでsqlite3の非同期関数の処理順序を制御できない

amesta122

総合スコア2

Electron

Electronは、HTML5とNode.jsというWebの技術を用いてデスクトップアプリケーションを作成できるクロスプラットフォームな実行環境です。

SQLite

SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

0グッド

0クリップ

投稿2021/04/07 22:24

前提・実現したいこと

Electronを使ってSQLiteのDBに接続していますが、
以下のようなイメージでall関数が確実に実行された後にクエリ結果を返したいです。
DBへの接続やクエリ自体に問題がないことは確認できています。

javascript

1 getdata : (query) => { 2 let result = []; 3 db.all(query, [],(err, rows ) => { 4 result = rows; //rowsは該当分の配列が返されています 5 }); 6 db.close(); 7 return result; 8 }

しかし、実際にはreturnが実行された後にdb.allの結果が返ってきてしまいます。

試したこと

sqliteのメソッドは非同期で実行されていることがわかり、callback関数でdb.allを制御しようと試みましたがdb.allメソッドがどのように動いているのかがわからず順序を制御できていません。
コールバック関数を調べて下記のように実装してみましたがreturnを実行した後にdb.allが実行されてしまいます。

javascript

1 getdata : (query) => { 2 const connect = (function() { 3 db.all(query, [], (err, rows) => { 4 return rows; //後にクエリ結果を返してしまう 5 }); 6 db.close(); 7 }); 8 return (function(callback){ 9 return callback(); 10 }(connect)); //先にundefinedを返してしまう 11 }

Chromeのデバッガでステップ実行すると先にクエリ結果を返してくれますが、本番環境だとundefinedを返した後にクエリ結果が返ってしまいます。closeが先行して落ちてしまう時すらあります。
どのようにコールバック処理を書けば、結果を返すまで確実に待つ処理を実装できるのかご教授下さい。

補足情報(ツールのバージョンなど)

Electron v12.0.1
Chromium v89.0.4389.82
Node v14.16.0v8
v8.9.255.20-electron.0
sqlite3@5.0.2

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

better-sqlite3に移行しては?

Easy-to-use synchronous API (better concurrency than an asynchronous API... yes, you read that correctly)

非同期ライブラリじゃなくて同期ライブラリだから使いやすいよって言ってます。


現状のコードを改修する形でやるなら
そもそもの前提知識が不足しているので
非同期処理についてちゃんと学習する必要があります

  1. JS(Node.js含む)の非同期処理の動作順番
  2. どうすれば目的の挙動をするコードを書けるのか
  3. Promiseで置き換える
  4. Promiseをasync/await構文で置き換える

上記4項目、上から順番に理解していけば問題は解消されますが、
調べもの・学習が捗るように軽く解説していきます。

1. JS(Node.js含む)の非同期処理の動作順番

JSはイベント駆動という話を聞いた事があると思います。
HDDやネットワーク越しの通信など、何時終わるか分からんクソ遅い挙動や、
Webサイトを訪問したユーザーのマウスクリックやスクロール状況によって初めて動作して欲しい挙動

JSの本来の目的はHTML(DOMツリー)を後から編集するためのスクリプト言語なので
JSは「待つ」事が求められました。
そのため「イベント駆動」という考え方を採用しています。

JSにはイベントループという概念が存在します。
イベント置き場に「達成条件」と「動作して欲しいコード片(関数)」をセットにして、
「イベント登録」を行います。

このイベント登録時、JSは「はいはい、イベント登録ね、後で見とくわ」とスルーします。
そして全てのJSコードの実行が終わってから達成しているイベントを巡回して、対になっている関数を実行してまわります。

例として下記のような0秒待つコードを書いてみましょう。
その場で条件達成しているのですが、
イベント登録だけでスルーされて、後のイベントループで抽出されるので後から実行される事が確認できます。

js

1console.log(1); 2setTimeout( 3 () => console.log(2), 4 0 5); 6console.log(3); 7// 1 -> 3 -> 2の順番に表示される

まぁ、ここまでは勉強出来てる内容通りです。

2. どうすれば目的の挙動をするコードを書けるのか

一度非同期の世界に入ったら
イベントループの先になるので同期処理には戻れません。
なので、このメソッド自体をコールバック対応関数として作成する必要があります。

その上でやりたいコードを全部関数の中にぶち込む事になります。
こんな感じ

js

1const obj = { 2 getdata : (query, cb) => { 3 db.all(query, [], (err, rows) => { 4 db.close(); 5 cb(err, rows); // コールバック関数の第一引数にエラー状況を埋める慣習がある 6 }); 7 } 8}

いやいや、結局そこでそうなるんじゃ何の意味もないでしょ。

でしょうね。
一生コールバック関数がネストしていく所謂「コールバック地獄」に陥ります。
これを解決させる為にJS使い達は苦戦を強いられてきました。

こういう数珠つなぎで長い非同期コードを管理する為のベストプラクティスが2015年以降の新しいJSで実装されました
後続のPromiseとasync/await構文です。

3. Promiseで置き換える

Promise - MDN

js

1const obj = { 2 getdata : (query, val = []) => new Promise((resolve, reject) => { 3 db.all(query, val, (err, rows) => { 4 db.close(); 5 if (err) { 6 return reject(err); 7 } 8 resolve(rows); 9 }); 10 }) 11}

昔ながらの非同期処理のコールバックをPromiseで扱う
bluebirdのようなライブラリも存在します。

ですがまぁ、Promiseもイベント登録→イベントループの先で実行するという制約を乗り越えた訳ではないので、
コールバック地獄でネストしまくって100文字インデントした後に処理を記述する事から逃げられた代わりに
.thenメソッドを叩きまくるというコールバック地獄のもう一つの問題は解消されていません。

なのでasync/await構文まできっちり押さえないと
やりたい事にはたどり着けません。
頑張ってください。

4. Promiseをasync/await構文で置き換える

非同期関数(async) - MDN
await演算子 - MDN

これらは内部処理でPromiseを上手く扱って、
同期処理っぽいコードが書ける糖衣構文です。
早速上記のPromise化したgetdataを扱ってみましょう。

js

1const obj = { 2 getdata : (query, val = []) => new Promise((resolve, reject) => { 3 db.all(query, val, (err, rows) => { 4 db.close(); 5 if (err) { 6 return reject(err); 7 } 8 resolve(rows); 9 }); 10 }) 11} 12 13// awaitを使う為にはasync関数を宣言する必要がある 14const main = async () => { 15 // awaitにはPromise.thenメソッドを叩いて第一引数を取り出す機能が存在する 16 const rows = await obj.getdata("SELECT * FROM users"); 17 console.log(rows); 18 19 // awaitはfor文の中でも使える 20 for (const id of [1, 2, 3]) { 21 const result = await obj.getdata("SELECT * FROM users WHERE id = ?", [id]); 22 console.log(result); 23 } 24 25 // catchメソッドに該当するコードが書きたい場合、try-catchでくくる 26 try { 27 const rows = await obj.getdata("SELECT * FROM foo"); 28 console.log(rows); 29 } catch (e) { 30 console.error(e); 31 } 32 33 // async関数内でreturnした場合、返り値は必ずPromiseになる事に注意 34 // 下記の123という値を取り出す時はthenメソッドかawait構文を使う必要がある 35 return 123; 36}

投稿2021/04/08 03:34

miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

amesta122

2021/04/09 23:21

すごい丁寧な解説で大変感謝してます。 引数の細かい修正や習慣も勉強になります。 JavaScriptに不慣れで泣きそうでしたが、おかげさまでC#で書いたawaitを思い出して解決し、背景も理解できました。 awaitがあるからthenを書いてないこと、そのまま出たresolve(rows)が欲しかったreturnの役割を果たすのが重要みたいですね。 自分が書いたコードを自分よりはるかにうまく操れていて尊敬します。もっと頑張ります。
guest

0

どのようにコールバック処理を書けば、結果を返すまで確実に待つ処理を実装できるのかご教授下さい。

JavaScriptの世界では、非同期処理の結果を同期的に返す方法は、基本的に存在しません

Promise(あるいはそれをラッピングしたasync-await)として、受け取る側に非同期処理を持ち越すよりありません。

投稿2021/04/07 22:54

maisumakun

総合スコア145123

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

amesta122

2021/04/09 23:21

ご指摘の通り、同期的にできれば何とかなると近眼的になってしまった感がありました。。。 getdataメソッド自体をPromiseでラッピング⇒awaitで確かに動きました。ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問