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

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

ただいまの
回答率

87.59%

JavaScriptの非同期処理に関して

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 969

score 106

疑問に思っている事

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

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

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • kei344

    2019/02/16 22:28

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

    キャンセル

  • papi_tokei

    2019/02/17 13:36

    コメント頂きありがとうございます。
    曖昧な質問で申し訳ございません。

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

    キャンセル

回答 2

+1

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

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

追記

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

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

 

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/17 14:13

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

    キャンセル

  • 2019/02/17 14:14

    ああ、直ってますね。
    ```JavaScriptでなくて、```HTMLだと思います。

    キャンセル

  • 2019/02/17 14:16

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

    キャンセル

checkベストアンサー

0

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

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

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

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

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

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

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

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

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

countdown.js

function countDown(seconds, label, cb) {
  var count = 0
  const tid = setInterval(eachSecond, 1000);

  function eachSecond() {
    count += 1
    console.log(`${label}: count is ${count}`)
    if (count >= seconds) {
      clearInterval(tid)
      cb(label)
    }
  }
}

const cb = x => console.log(`${x} expired`)
countDown(3, "A", cb)
countDown(3, "B", cb)
$ node countdown.js
A: count is 1
B: count is 1
A: count is 2
B: count is 2
A: count is 3
A expired
B: count is 3
B expired
$


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

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

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

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

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

const EventEmitter = require('events').EventEmitter
let clockTick = 0

function asyncRun(g, interval) {
  const ee = new EventEmitter()

  ee.on('next', () => {
    if (!g.next().done) {
      step()
    }
  })

  function step() {
    setTimeout(() => ee.emit('next'), interval)
  }

  step()
}

function* foo(n, cb) {
  console.log('foo: invoked')
  let s = 0
  for (let i = 1; i <= n; i++) {
    console.log(`foo: clockTick=${clockTick}: i=${i}`)
    s += i
    yield
  }
  cb(s)
  console.log('foo: ends')
}

function asyncFoo(n, cb) {
  asyncRun(foo(n, cb), 10)
}

asyncFoo(5, r => console.log(`callback: result=${r}`))

const tid = setInterval(() => {
  clockTick += 1
  if (clockTick > 1000) {
    clearInterval(tid)
    console.log('interval timer stopped')
  }
}, 0)
console.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/20 21:36

    非常に丁寧に回答頂きありがとうございます。
    JSからC++のコードを実行できるのですね。
    初めて知りました。

    基本的には非同期関数を自分で実装するというよりも、非同期関数をどのように使いこなしていくか、が重要だと理解しました。
    ありがとうございます。

    キャンセル

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

  • ただいまの回答率 87.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る