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

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

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

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

Q&A

解決済

2回答

1331閲覧

JavaScriptの非同期処理に関して

papi_tokei

総合スコア106

JavaScript

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

0グッド

1クリップ

投稿2019/02/16 12:47

疑問に思っている事

JavaScript(以降JSと表記)の非同期処理に関して疑問に思ったので質問させて頂きました。
JSの非同期処理に関して検索すると、非同期処理をどのように同期的に処理するのか、という内容の結果が多く見つかりますが、そもそも非同期関数を自分で実装する方法について記載されたサイトは見つかりませんでした。
もし、自分で非同期処理を行う関数を作成したい場合は、どのようにすればいいのでしょうか?

HTTP通信を実現するrequestというライブラリがありますが、この関数は非同期処理を実現している関数です(Gitに関数があるので、自力で読めればよいのですが、そこまでの能力はなく。。。)
私の勝手な想像なのですが、まず同期的にHTTPリクエストを発行し、結果を取り出す処理を少し後の実行キューに追加している、という感じなのでしょうか?

JSはシングルスレッドという話を聞いたことがあります。
ブラウザのJSとNode.jsともにそうであれば、少し後の実行キューにいれる、という処理でしか非同期を実現できないという私の考えは正しいでしょうか?

少し曖昧な質問で申し訳ないです。
もしよければ、回答頂ければ幸いです。
よろしくお願いいたします。

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

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

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

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

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

kei344

2019/02/16 13:28

どういうものを作りたい(作る必要がある)と思っているのでしょうか。そこを明確にされたほうがよいと思います。
papi_tokei

2019/02/17 04:36

コメント頂きありがとうございます。 曖昧な質問で申し訳ございません。 具体的にどのようなものが作りたい、というイメージはないのですが、HTTPリクエストの関数やファイル読み込みの関数、AWSのSDKなどの関数は非同期で動作します。 なので、もし自分で上記で挙げたような関数を自作したい場合は、非同期処理の実装をどのようにすればいいのかな、と疑問に思ったのでご質問させて頂きました。
guest

回答2

0

ベストアンサー

自分で非同期処理を行う関数を作成したい場合は、どのようにすればいいのでしょうか?

自分が考える「非同期処理を行う関数」の本質的なイメージは

(A) ある仕事をするための関数がその仕事が終わらないうちに呼び出し元へ戻ってくるもの

です。呼び出し元で仕事が終わったときに特定のことをしたいことも多いので上記に加え

(B) その仕事が終わったら指定したコールバック関数を自動的に呼び出してくれるもの

という条件を加えてもよいでしょう。

(1) 普通のケース(I/Oバウンドな処理の非同期関数)

世の中のJavaScriptのプログラマーは普段から自然に非同期な関数を書いていると思います。どうやって書いているかといえば単に「より低水準な非同期関数を利用しているだけ」といえましょう。ブラウザーにせよNodeJSにせよシングルスレッドによる非同期処理が基本戦略ですので非同期関数は豊富に用意されています。非同期関数を定義するというのは「JavaScriptで何かを書くこと」とほとんど同義であるといっても過言ではないと思います。

非同期な関数を利用して非同期な関数を定義する素朴な例を挙げますと

countdown.js

js

1function countDown(seconds, label, cb) { 2 var count = 0 3 const tid = setInterval(eachSecond, 1000); 4 5 function eachSecond() { 6 count += 1 7 console.log(`${label}: count is ${count}`) 8 if (count >= seconds) { 9 clearInterval(tid) 10 cb(label) 11 } 12 } 13} 14 15const cb = x => console.log(`${x} expired`) 16countDown(3, "A", cb) 17countDown(3, "B", cb)

bash

1$ node countdown.js 2A: count is 1 3B: count is 1 4A: count is 2 5B: count is 2 6A: count is 3 7A expired 8B: count is 3 9B expired 10$

setIntervalを利用したcountDown関数もまた非同期関数になりcountDownを2回コールするとそれらが並行して動作していることがわかります。

requestも本質的にはこういう類のものと思います。ソケットへの非同期I/Oをしてくれる低水準な非同期関数を用いてより高水準な非同期関数を定義しているイメージです。

(2) CPUバウンドな非同期関数

あまり一般的とは言えない例だと思いますが、(1)のようなI/OではなくJavaScriptそのもので大量の計算処理を行うような機能を非同期にしたい場合、シングルスレッドで行う方法と別のスレッド(または別のプロセス)を用いる方法が考えられると思います。

JavaScriptの主たる計算モデルであるシングルスレッドで行うなら「計算全体を細切れにして一時にほんの少しだけ計算を進める」という戦略を用い例えば次のようなすると非同期関数となります。

js

1const EventEmitter = require('events').EventEmitter 2let clockTick = 0 3 4function asyncRun(g, interval) { 5 const ee = new EventEmitter() 6 7 ee.on('next', () => { 8 if (!g.next().done) { 9 step() 10 } 11 }) 12 13 function step() { 14 setTimeout(() => ee.emit('next'), interval) 15 } 16 17 step() 18} 19 20function* foo(n, cb) { 21 console.log('foo: invoked') 22 let s = 0 23 for (let i = 1; i <= n; i++) { 24 console.log(`foo: clockTick=${clockTick}: i=${i}`) 25 s += i 26 yield 27 } 28 cb(s) 29 console.log('foo: ends') 30} 31 32function asyncFoo(n, cb) { 33 asyncRun(foo(n, cb), 10) 34} 35 36asyncFoo(5, r => console.log(`callback: result=${r}`)) 37 38const tid = setInterval(() => { 39 clockTick += 1 40 if (clockTick > 1000) { 41 clearInterval(tid) 42 console.log('interval timer stopped') 43 } 44}, 0) 45console.log('main: last line')

まず同期的にHTTPリクエストを発行し、結果を取り出す処理を少し後の実行キューに追加している

という質問者さんのイメージに近いのが上の例だと思いますが前述したようにこの方式は一般的とは言えないでしょう。いわゆる普通の(?)非同期関数はどちらかといえばI/Oを非同期に行うものという印象が強いので「少し後の実行キューに追加」ではなく「望む状態になったら(いつ完了するかわからないI/O動作が完了したら)自動的かつ即座に実行キューに追加される」とイメージすることが多いのではないでしょうか。

ちなみに別のスレッドでやるならworker_threads、別のプロセスでやるならprocessといったモジュールを利用すれば実現できますがやはり「あまり一般的な方法とは言えない」と思います。

(3) もっと低水準なもの

(1)(2)とは異なりJavaScriptから利用できる適切な関数が存在しないようなケースがあるかも知れません。そういう場合C/C++で実装した実現が可能です(少なくともNodeJSではそれが容易にできます)。細かくは言及しませんが、例えば「nodejs c++ 呼び出し」で検索するとNANというライブラリーがありNodeJSからC/C++で記述したプログラムと連携する方法があるということがわかります。

https://qiita.com/sassy_watson/items/25241868be12a425f86a

C/C++で実装するものですのでJavaScriptに望む機能がなくてもC/C++から利用できる多くの機能(例えばOSに対する直接的なシステムコールのようなもの)も利用でき「およそ計算機で実現可能なことならなんでもできる」といってよいと思います。


一番言いたいこと:

いくつかの例(1),(2),(3)を挙げましたが、おそらく質問者さん(あるいは多くのJavaScriptのアプリケーションプログラマー)にとって(2)や(3)のような方法が必要な場面はほぼないと思います。なぜなら「JavaScriptでやりたいような多くの機能」は既にだれかがJavaScriptの非同期関数として開発しているからです。音声・アニメーション・ファイルとのI/O・ネットワークを介した通信などおよそ必要と思われるほとんどの基本機能がブラウザーなりNodeJSで利用可能になっていますので普通のアプリケーションプログラマーは、主に(1)の方法でアプリケーションを(非同期関数の集まりとして)書いているだろうと思います。

要するに「非同期関数をどうやって定義するか」は「既に存在している非同期関数をどうやって使うか」つまり「JavaScriptでアプリケーションをどうやって書くか」と同義と思います。自分が一番いいたかったのはそういうことです。

投稿2019/02/19 04:57

KSwordOfHaste

総合スコア18392

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

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

papi_tokei

2019/02/20 12:36

非常に丁寧に回答頂きありがとうございます。 JSからC++のコードを実行できるのですね。 初めて知りました。 基本的には非同期関数を自分で実装するというよりも、非同期関数をどのように使いこなしていくか、が重要だと理解しました。 ありがとうございます。
guest

0

答え方に困る質問ですが、単に非同期読み込みしたいならscriptタグにasnyc属性を追加するだけです。

HTML

1<script src="test.js" async></script>

追記

また見落としてました。
関数ですか...

HTML

1<script> 2var script = document.createElement('script'); 3 script.src = "test.js"; 4 document.getElementsByTagName('head')[0].appendChild(script); 5</script>

見当違いでしたらすみません
引用元(一部改変)

投稿2019/02/16 14:23

編集2019/02/17 05:16
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

papi_tokei

2019/02/17 04:41

回答頂きありがとうございます。 スクリプトを非同期で読み込みことが出来たのですね。 勉強になりました。
think49

2019/02/17 04:45

@yoshi0819 さん markdownの使い方を間違っていませんか。 <blockquote> で括られた文章は、引用文に読めません。 <code> を指定したかったのでは?
退会済みユーザー

退会済みユーザー

2019/02/17 05:06

初めて知りました ありがとうございます。
think49

2019/02/17 05:11

markdown書式をミスしているようです。 > ```HTML > <script src="test.js" async></script> > ``` こうします。
退会済みユーザー

退会済みユーザー

2019/02/17 05:12

今気づきました ご指摘ありがとうございます。
think49

2019/02/17 05:13

コードブロックや引用ブロックは前後に空行が必要です。 空行がないと後続文も繋げてしまいます(文法からすればバグっぽいですが、そういう仕様なのでしょう)。
think49

2019/02/17 05:14

ああ、直ってますね。 ```JavaScriptでなくて、```HTMLだと思います。
退会済みユーザー

退会済みユーザー

2019/02/17 05:16

あっ 何度も何度もすみません...
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問