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

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

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

RxJSは、Observablesを用いたリアクティブプログラミングのJavaScript向けの実装です。イベント駆動処理も含めた非同期処理を高い可読性を持って容易にコーディングできます。

JavaScript

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

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

Q&A

解決済

2回答

3661閲覧

async awaitが意図した動きになりません

yoppy0066

総合スコア293

RxJS

RxJSは、Observablesを用いたリアクティブプログラミングのJavaScript向けの実装です。イベント駆動処理も含めた非同期処理を高い可読性を持って容易にコーディングできます。

JavaScript

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

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

0グッド

1クリップ

投稿2018/09/21 10:09

編集2018/09/21 10:13

ionic3(Angular4)でasync、awaitを使った同期処理でAPIへのリクエストを順次行う処理を実装しています。

以下のようなコードで、こちらの出力を期待しているのですが、

0 APIのレスポンス 1 APIのレスポンス 2 APIのレスポンス

このような形になってしまいます。何がまずいのでしょうか?

0 1 2 APIのレスポンス APIのレスポンス APIのレスポンス

コード

import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; import { Http, Headers,RequestOptions } from '@angular/http' import { Observable, Subject } from 'rxjs' import 'rxjs/add/operator/map' import 'rxjs/add/operator/catch' @Component({ selector: 'page-home', templateUrl: 'home.html' }) export class HomePage { constructor( public navCtrl: NavController, public http: Http ) { } ionViewDidEnter() { // 実験1 for (let i = 0; i < 3; i++) { (async () => { console.log(i) let result = await this.requestApi() console.log(result) })() } // 実験2 for (let i = 0; i < 3; i++) { console.log(i) this.requestApiAsync(result => console.log(result)) } } async requestApiAsync() { const result = await this.requestApi() return result } requestApi() { return new Promise(resolve => { let url = 'http://example.com/path/to' let body = 'key=value' let headers = new Headers({ "Content-Type": "application/x-www-form-urlencoded" }) let options = new RequestOptions({ headers: headers }) return this.http.post(url, body, options) .map(response => response) .catch(error => error) .subscribe(result => resolve(result.json())) }) } }

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

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

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

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

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

guest

回答2

0

ベストアンサー

そもそもasync/awaitは同期的に動作するわけではなく、
単なるPromiseの糖衣構文なので、awaitの右辺にあるpromiseに以降の文を関数化して.then(fn)でくっつけた動作しかしません。

JSのイベントループの考え方の理解がまだ少し足りていませんね。
JSは大雑把に下の流れで駆動します。

  • JSは同期的な処理は全てその場で実行して終わらせる
  • 「ToDoとして関数化したやりたい事」をイベント発火条件に登録する
  • 実行が終わった後に、イベントループで巡回し、イベント発火条件を満たしたToDoになってる関数を実行

Promiseを作るというのは、イベント発火条件に関数作って登録しているだけであり、
for文をぐるぐる回す方がまず優先されます。
従って、ionViewDidEnterの2つの手法はどちらもPromiseを3つ作る事だけが目的ですので、
質問文に書いてあるように1〜3の文字が先に表示されます。

理想の形にしたければこう書くしかありません。
(まぁ抽象化の仕方とか考えてば色々工夫しがいはあるでしょうけどね)

JavaScript

1ionViewDidEnter() { 2 console.log(1) 3 this.requestApiAsync(result => console.log(result)) 4 .then(() => { 5 console.log(2) 6 return this.requestApiAsync(result => console.log(result)) 7 }) 8 .then(() => { 9 console.log(3) 10 return this.requestApiAsync(result => console.log(result)) 11 }) 12}

これは流石に酷いので現実はionViewDidEnterもasync関数として宣言し、
await構文を有効化させます。

JavaScript

1async ionViewDidEnter() { 2 for (let i = 1; i <= 3; i++) { 3 console.log(i); 4 await this.requestApiAsync(result => console.log(result)) 5 } 6}

投稿2018/09/21 10:29

miyabi-sun

総合スコア21194

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

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

yoppy0066

2018/09/21 10:36

ありがとうございました!
guest

0

実験1でいうと、for文中の匿名関数内での実行順はちゃんとawaitされていますが、
匿名関数の実行自体はawaitされておらず、
前のループの匿名関数の完了を待たずに次のループに入ってしまってます。
なので例えば...

const foo = () => new Promise(resolve => setTimeout(resolve('foo'), 1000)) const bar = async () => { for (let i = 0; i < 3; i++) { await (async () => { // <-- ここにawaitがあると想定どおりになります console.log(i) let result = await foo() console.log(result) })() } } bar()

追記: 読み返すと冗長でした。リファクタ後↓↓↓

const bar = async () => { for (let i = 0; i < 3; i++) { console.log(i) let result = await foo() console.log(result) } }

投稿2018/09/21 10:27

編集2018/09/21 10:34
set0gut1

総合スコア2413

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

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

yoppy0066

2018/09/21 10:37

ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問