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

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

ただいまの
回答率

90.99%

  • JavaScript

    13876questions

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

  • Node.js

    1560questions

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

  • Ajax

    958questions

    Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

  • npm

    208questions

    npmは、Node Packaged Modulesの略。Node.jsのライブラリ・パッケージを管理できるツールです。様々なモジュールを簡単にインストールでき、自分でモジュールを作成し公開する際にも使用できます。

  • Express

    185questions

    ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

非同期通信でレスポンスがあったものを指定の順番に出力するロジック

受付中

回答 9

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 747
退会済みユーザー

退会済みユーザー

以下のコードを用いてライブラリやPromiseなどを使わずに、5つのurlに対して次々にほぼ同時にリクエストを送って、「1.txt、2.txt、3.txt、4.txt、5.txt」と指定した順番にレスポンス結果を出力し、最後に「all done!」と出力する為には、みなさんならどのような書き方をしますか?

注意点としては、1つの目のリクエストのレスポンスを受けた後に、次のリクエストを送るといった順列処理ではなく、最初に5つのリクエストをほぼ同時に行って、指定の順序でレスポンスを出力するというところです。

補足:KSwordOfHasteさんの回答にあったように、「結果が揃っている部分だけはできるだけ早く(しかし順番を守って)処理したい」という想定です。

また、以下に掲載するコードを使ってください。どうしても使いたくない場合は、それはそれでOKとします。。。以下の掲載コードにある関数外にコードを書くのはOKです。

また、前述の通り、ライブラリやPromiseなどを使うのはNGとします。

尚、自分も後で自分なりのやり方を投稿しますが、やり方を教えて欲しいといった類の質問ではなく、どういったやり方をみなさんがされるかに興味があるため、「コードを載せてください」といった趣旨に沿わないコメントは不要です。

// ajaxのフェイクバージョンのつもり
function fakeAjax(url, callback){
    //レスポンスまでのランダムなタイムラグをシミュレート
    var time = Math.floor(Math.random()*(3000-1000)+1000);
    var responseMock = {
        "1.txt": "1st request",
        "2.txt": "2nd request",
        "3.txt": "3rd request",
        "4.txt": "4th request",
        "5.txt": "5th request"
    };
    setTimeout(() => {
        callback(responseMock[url]);
    }, time);
}

function getFile(url){
    fakeAjax(url, function(response){
        //
    });
}

getFile("1.txt");
getFile("2.txt");
getFile("3.txt");
getFile("4.txt");
getFile("5.txt");

 期待するログ出力の結果(console.log)

1st request
2nd request
3rd request
4th request
5th request
all done!

 補足

低評価を押す場合は理由を添えてください。マイナス評価が多いにも関わらず、質問の趣旨に沿った物凄く有益な回答を頂いており、価値あるページになっていると思います。あえて、質問の趣旨に意図的に沿わない回答をする必要はないかと思いますので、的外れな回答はお控えください。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • yambejp

    2017/11/24 21:59

    ここまで広げたら削除するのはどうかと思いますよ・・・最初の約束通りご自身の解を提示してBAなしでいいのでクローズしてください

    キャンセル

  • 退会済みユーザー

    退会済みユーザー

    2017/11/25 19:10

    いや、中の人に非推薦と言われたので削除が妥当なのかなと思いまして。

    キャンセル

  • Lhankor_Mhy

    2017/11/27 14:04

    削除依頼の結果はいかがでしたか? 削除されるのであれば、他のユーザーがこの質問を見て回答を考えるリソースが無駄ですので、お早めに。……とはいえ、仕様上、-6の評価の質問がフィードに載ることはあまりない( https://teratail.com/blog/article/ba22#-1 )でしょうから、現実的にはその心配はないでしょうけれど。

    キャンセル

回答 9

+4

そもそもteratailは有益な情報を共有する場であって、
質問者のためのオーダメイドのコードを書く場とは違います。
低評価が付いてるのも、そういう理由からだと思います。

実務では「ライブラリやPromise」を素直に使えば良いと思います。
ただ、それ以外の方法もあるので、一応書いてみました。

上記の理由で、ご質問の細かい仕様はあえて無視し、
不特定多数で共有しやすいように、シンプルに書きました。


function fakeAjax(g) {
    var time = Math.floor(Math.random() * 2000 + 1000);
    setTimeout(function() {
        console.log(g.next());
    }, time);
}

var g = (function*() {
    yield "1st request";
    yield "2nd request";
    yield "3rd request";
    yield "4th request";
    yield "5th request";
    return "all done!";
})();

for (i = 0; i < 6; i++) {
    fakeAjax(g);
}

ほぼES6前提ですが、「Generator」を使うと、
コールバック地獄のような深いネストを避けて、
非同期処理を非常にシンプルに書くことができます。

なお余談ですが、RubyやPythonをやっていると、
「yield」や「Generator」のような概念は馴染み深いです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/11/19 21:47 編集

    ご回答ありがとうございました。Generatorもasync awaitもObservableもNGと書いておけばよかったです。。。まさかGeneratorを使ってくるとはw
    それらを使えば簡単にかけてしまうのは分かってるのですが、あえてというところです。

    キャンセル

  • 2017/11/19 21:43

    teratailが有益な情報を共有する場であるならば、なんの問題もないと思うのですが、感情的に気にくわない類の内容なんでしょうね。

    キャンセル

  • 2017/11/19 22:28

    上のコメントとも関連しますが、teratailはあくまで「質問する方の問題解決」の場だ、とのことです。

    キャンセル

  • 2017/11/19 23:03

    >teraterakunさん
    >maisumakunさん
    コメントありがとうございます。

    質問への低評価の理由を推測すると、
    Promiseなどを禁じ手にする動機が不明で、
    個人的なこだわりに思えるからだと思います。

    回答者の方の具体的な問題解決を通じて、
    有益な情報をみんなで共有できるのが理想です。
    それで、Generatorは共有できそうなので書きました。

    キャンセル

  • 2017/11/19 23:30

    このgeneratorの簡易サンプルは前のリクエストのレスポンスが返ってきたら、次のリクエストを投げるというサンプルですか?それとも、掲題の仕様を一応、想定してのものでしょうか?

    キャンセル

  • 2017/11/20 00:13 編集

    後者です。前のレスポンスを待ってないですよ。
    ジェネレータで非同期処理に対応可能です。

    キャンセル

  • 2017/11/20 09:51

    その場合はGeneratorを使うにしても、質問の要件を満たしていることがわかるように最低限書いて頂きたかったです。さすがに高評価トップは他の回答者様に申し訳なくないですか?

    キャンセル

+4

maisumakunさんやLLmanさんのおっしゃることはもっともであると思いましたが、Javascriptの練習のつもりでやってみました。

本件に特化した単純な実装を想定すると、Promise.all(...).then(...)でthenのところで、全ての結果を出力するようなものの特化版(下記)をイメージしましたが、

リスト1:

const dbg = m => console.log('information: ' + m);

function fakeAjax(url, callback){
  var time = Math.floor(Math.random()*(300-100)+100);
  var responseMock = {
    "1.txt": "1st request",
    "2.txt": "2nd request",
    "3.txt": "3rd request",
    "4.txt": "4th request",
    "5.txt": "5th request"
  };

  setTimeout(() => {
    dbg('completed ' + url);
    callback(responseMock[url]);
  }, time);
  dbg('submitted ' + url);
}

function getFiles(urls) {
  var n = urls.length, res = new Array(n),
      i, responseCount = 0;
  for (i in urls) {
    getFile(i, urls[i]);
  }

  function getFile(i, url) {
    fakeAjax(url, function (response) {
      res[i] = response;
      if (++responseCount >= n)
        allDone();
    });
  }

  function allDone() {
    var i;
    for (i in urls) {
      console.log(res[i]);
    }
    console.log("all done!");
  }
}

getFiles(["1.txt", "2.txt", "3.txt", "4.txt", "5.txt"]);

しかしこれだとPromise.allを使わない根拠がない気がしたので、「結果が揃っている部分だけはできるだけ早く(しかし順番を守って)処理したい」という想定を置くと

リスト2:

// fakeAjaxは省略
// 若干の見易さのためにES2015の文法を用いました

class AsyncContext {
  constructor() {
    this.acceptable = true;
    this.q = [];
  }

  getFile(url, postProcess) {
    if (!this.acceptable)
      throw new Error('submitting queue has already been closed');
    const task = { postProcess: postProcess };
    this.q.push(task);
    fakeAjax(url, result => {
      task.result = result;
      this.doPostProcess();
    });
  }

  doPostProcess() {
    while (this.q.length > 0 && this.q[0].hasOwnProperty('result')) {
      const task = this.q.shift();
      task.postProcess(task.result);
      if (!this.acceptable && this.q.length === 0)
        console.log('all done!');
    }
  }

  close() {
    this.acceptable = false;
    this.doPostProcess();
  }
}

const ctx = new AsyncContext();

[
  "1.txt",
  "2.txt",
  "3.txt",
  "4.txt",
  "5.txt"
].forEach(url => ctx.getFile(url, console.log));
ctx.close();


質問のコードテンプレートだとgetFileが必要とするコンテキストを関数の外側に用意しなければならないためそれは断念しコンテキスト保持のためのオブジェクトを用いました。

なお起動する非同期処理は決め打ちにもかかわらず後処理だけctx.getFileの引数にしている点は中途半端かつ機能分離がイケテナイ気がしました。このようなものを書くならfakeAjaxと機能を明確に分離すべきかも知れません。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/11/20 09:29 編集

    ご回答ありがとうございます。
    >「結果が揃っている部分だけはできるだけ早く(しかし順番を守って)処理したい」という想定
    こちらの想定で合っております。後ほど興味深く拝見させて頂きます。取り急ぎお礼まで。

    キャンセル

+3

ゴチャゴチャしたコードになってしまいましたが...。

const urls = ["1.txt", "2.txt", "3.txt", "4.txt", "5.txt"]
const buffers = urls.map(url => {
  const buffer = {done: false, result: undefined, onComplete: undefined}
  fakeAjax(url, result => {
    buffer.done = true
    buffer.result = result
    if (buffer.onComplete) buffer.onComplete(result)
  })
  return buffer
})

function waitFor(index, onAllComplete) {
  function printAndNext(val) {
    console.log(val)
    if (index < urls.length - 1) {
      waitFor(index + 1, onAllComplete)
    } else {
      if (onAllComplete) onAllComplete()
    }
  }
  const bf = buffers[index]
  if (bf.done) {
    printAndNext(bf.result)
    return
  }
  bf.onComplete = result => {
    printAndNext(result)
  }
}

waitFor(0, () => {
  console.log("all done!")
})

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/11/19 21:44

    ご回答ありがとうございます。動作確認を早く家に帰ってしたいです!

    キャンセル

+3

一切ライブラリのない、純粋なJavaScriptでは実装不可能です。

「JavaScript+言語標準で存在するオブジェクト」では、通信機能もタイマー機能もありません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/11/20 11:49 編集

    蛇足と思いますが、気になるのでコメントさせていただきます。本件について自分は「ひょっとするとこの想定があるかも」と気づきましたが、それはどちらかと言えば他の回答から得たところが大きく残念ながら質問文から明確に受け取ることはできませんでした。現在の質問文は当初に対し前提をより明確にしておられますが、それはmaisumakunさんや他の方の(課題・前提が不明瞭という)示唆が当を得ていた気がしました。
    ---
    えと、これは質問者さんへのコメントです。わかりにくくなってしまいましたが・・・

    キャンセル

+3

 1

イメージ説明
アニメーションGIF

const fakeAjax = (fileName, callback) => {
    const MAX = 5000;
    const MIN = 1000;
    const delay = Math.floor(Math.random() * (MAX - MIN) + MIN);

    const responseMockData = {
        "1.txt": "1st request",
        "2.txt": "2nd request",
        "3.txt": "3rd request",
        "4.txt": "4th request",
        "5.txt": "5th request"
    };
    setTimeout(() => {
        callback(responseMockData[fileName]);
    }, delay);
}

const output = console.log.bind(this);

const getFile = (fileName) => {
    fakeAjax(fileName, (response) => {
        handleResponse(fileName, response);
    });
}

const STATUS = Object.freeze({
    WAITING: Symbol('waiting'),
    RECEIVED: Symbol('received'),
    RENDERED: Symbol('rendered')    
});


const responseItems = new Map([
    ["1.txt", { status: STATUS.WAITING, content: ''}],
    ["2.txt", { status: STATUS.WAITING, content: ''}],
    ["3.txt", { status: STATUS.WAITING, content: ''}],
    ["4.txt", { status: STATUS.WAITING, content: ''}],
    ["5.txt", { status: STATUS.WAITING, content: ''}]
]);

const handleResponse = (fileName, response) => {

    if (responseItems.get(fileName).status === STATUS.WAITING) {
        responseItems.get(fileName).status = STATUS.RECEIVED;
        responseItems.get(fileName).content = response;
    }

    for (const key of responseItems.keys()) {

        if (responseItems.get(key).status === STATUS.RECEIVED) {
            output(responseItems.get(key).content);
            responseItems.get(key).status = STATUS.RENDERED;
        } else if(responseItems.get(key).status === STATUS.RENDERED) {
            continue;
        } else {
            return;
        }

    };

    console.log('all done!')

};


getFile("1.txt");
getFile("2.txt");
getFile("3.txt");
getFile("4.txt");
getFile("5.txt");

2

イメージ説明
アニメーションGIF

const fakeAjax = (fileName, callback) => {
    const MAX = 5000;
    const MIN = 1000;
    const delay = Math.floor(Math.random() * (MAX - MIN) + MIN);

    const responseMockData = {
        "1.txt": "1st request",
        "2.txt": "2nd request",
        "3.txt": "3rd request",
        "4.txt": "4th request",
        "5.txt": "5th request"
    };

    setTimeout(() => {
        callback(responseMockData[fileName]);
    }, delay);
}

const output = console.log.bind(this);

const getFile = (fileName) => {
    let memoizedResult;
    let memoizedCallback;

    fakeAjax(fileName, (response) => {
        if (memoizedCallback) {
            return memoizedCallback(response);
        }
        memoizedResult = response;
    });

    return (callback) => {
        if (memoizedResult) {
            return callback(memoizedResult);
        }
         memoizedCallback = callback;
    };
}

const getFile1 = getFile('1.txt');
const getFile2 = getFile('2.txt');
const getFile3 = getFile('3.txt');
const getFile4 = getFile('4.txt');
const getFile5 = getFile('5.txt');


getFile1(resultForFile1 => {
    output(resultForFile1);
    getFile2(resultForFile2 => {
        output(resultForFile2);
        getFile3(resultForFile3 => {
            output(resultForFile3);
            getFile4(resultForFile4 => {
                output(resultForFile4);
                getFile5(resultForFile5 => {
                    output(resultForFile5);
                    output('all done!');
                })
            })
        })
    })
})

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/11/20 09:29 編集

    ご回答ありがとうございます。後ほど興味深く拝見させて頂きます。取り急ぎお礼まで。

    キャンセル

+3

つまり、変なコード書いた奴が一等賞ってことですね!
負けないぞう!

function fakeAjax(url, callback){
    //レスポンスまでのランダムなタイムラグをシミュレート
    var time = Math.floor(Math.random()*(3000-1000)+1000);
    var responseMock = {
        "1.txt": "1st request",
        "2.txt": "2nd request",
        "3.txt": "3rd request",
        "4.txt": "4th request",
        "5.txt": "5th request"
    };
    setTimeout(() => {
        callback(responseMock[url]);
    }, time);
}

function getFile(url){
    q[url] = null;
    fakeAjax(url, function(response){
        q[url] = response;
        [...q].forEach( x => console.log(x) );
    });
}

const q = new Proxy([],{
  get: function(obj, prop) {
    if (prop===Symbol.iterator) {
      if (obj.length) return function*(){
        let x;
        while( x = obj.shift() ) {
          if (x.v) 
            yield x.v
          else {
            obj.unshift(x);
            break;
          }
        }
        if (!x) yield 'all done!';
      };
    }
    return obj.filter( x => x.k == prop )[0].v;
  },
  set: function(obj, prop, value){
    let entry = obj.filter( x => x.k == prop )[0];
    if (entry) 
      entry.v = value
    else
      obj.push({k:prop, v:value});
  },
});

getFile("1.txt");
getFile("2.txt");
getFile("3.txt");
getFile("4.txt");
getFile("5.txt");

不要と思いますが解説。
[{k:'1.txt', v:'1st request'}, ...]のような構造体を作り、Proxyにより独自アクセサを設定することによって、順序型であることを意識せずにプロパティにアクセスするようにしました。
また、Symbol.iteratorgetterに出力の際のめんどくさい処理を設定することにより、列挙するだけで出力が得られるようになっています。
その結果、元コードに追加したのは3行のみ、というシンプルなコードになったと思います。
(Falsyな値が来るといろいろバグりますけど) 
 

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/11/20 23:05

    ご回答ありがとうございます。変なコードを他の回答者様は書いてるつもりはないかと思います。

    キャンセル

  • 2017/11/21 01:02

    いや、あの、実用性のない趣味に走ったコード、という意味ですよ。
    コードゴルフみたいなものでしょ?

    キャンセル

  • 2017/11/21 14:30

    まぁ大喜利ですよね

    キャンセル

  • 2017/11/21 20:42

    ですねー

    キャンセル

+2

CoffeeScriptで書いてみました。

# ajaxのフェイクバージョンのつもり
fakeAjax = (url, callback) ->
  # レスポンスまでのランダムなタイムラグをシミュレート
  time = Math.floor(Math.random() * (3000 - 1000) + 1000)
  responseMock =
    '1.txt': '1st request'
    '2.txt': '2nd request'
    '3.txt': '3rd request'
    '4.txt': '4th request'
    '5.txt': '5th request'
  setTimeout ->
    callback(responseMock[url])
  , time

getFile = (url) ->
  new Promise (resolve) ->
    fakeAjax url, (response) ->
      resolve(response)

[
  getFile('1.txt')
  getFile('2.txt')
  getFile('3.txt')
  getFile('4.txt')
  getFile('5.txt')
  Promise.resolve('all done!')
].reduce (pre, curr) ->
  pre.then (v) ->
    console.log(v)
    curr
.then console.log

レギュレーション違反「Promiseの使用」により失格!


Promiseが禁止なら、擬きを作れば良い。

# 約束擬き
class PseudoPromise
  constructor: (executor) ->
    executor(@resolve)

  then: (func) ->
    new PseudoPromise (resolve) =>
      @fulifillment = (value) ->
        result = func(value)
        if result instanceof PseudoPromise
          result.then (resultValue) ->
            resolve(resultValue)
        else
          resolve(result)
      @fulfill()

  resolve: (value) =>
    @value = value
    @fulfill()

  fulfill: () ->
    if @value? and @fulifillment?
      @fulifillment(@value)

  @resolve = (value) ->
    new PseudoPromise (resolve) ->
      resolve(value)

# ajaxのフェイクバージョンのつもり
fakeAjax = (url, callback) ->
  # console.log "start: #{url}"
  # レスポンスまでのランダムなタイムラグをシミュレート
  time = Math.floor(Math.random() * (3000 - 1000) + 1000)
  responseMock =
    '1.txt': '1st request'
    '2.txt': '2nd request'
    '3.txt': '3rd request'
    '4.txt': '4th request'
    '5.txt': '5th request'
  setTimeout ->
    # console.log "end: #{url}"
    callback(responseMock[url])
  , time

getFile = (url) ->
  new PseudoPromise (resolve) ->
    fakeAjax url, (response) ->
      resolve(response)

[
  getFile('1.txt')
  getFile('2.txt')
  getFile('3.txt')
  getFile('4.txt')
  getFile('5.txt')
  PseudoPromise.resolve('all done!')
].reduce (pre, curr) ->
  pre.then (v) ->
    console.log(v)
    curr
.then console.log

ES5なJavaScriptでのデモ

とりあえずこのコードで動くための実装にしているため、かなり横着している部分があります(失敗する場合とか)。PromiseのPolyfillを頭に付けた方が良いと思いますが、CoffeeScriptの実装が見当たらなかったので、仕方が無く、それっぽいのを作りました。

審査委員A「これは、Promiseの使用にあたるのでは無いかね?」
審査委員B「後半のコードなど、先日の失格コードそのままでは無いか!これでは新しいコードとは言わん。」
審査委員C「それに、このPromise実装は不十分なところが多すぎる。実用的とはとても言えん。」
私「PseudoPromiseはPromiseじゃないよ、たとえPromiseだとしても、Promiseと言う名のクラスだよ!」
審査委員長「判定を言い渡す。ただのPromiseのパクリであり、創意工夫が見られないため失格とする。以上。」

(良い方法が思いついたら)PureScriptかOpalあたりで出直してきます・・・

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/11/19 21:16 編集

    あ、Promise禁止って・・・すいません。私には無理です。この回答は忘れて下さい。

    キャンセル

  • 2017/11/19 21:17

    お付き合い頂いてありがとうございましたm(_ _)m

    キャンセル

  • 2017/11/20 22:19

    「手元の道具が使えないのなら、代わりの道具を作ってしまえば良い。」の精神で頑張りました。実装までに一日考え込んでしまいました。ちょっと、難しかったです。

    キャンセル

  • 2017/11/20 22:21

    ついでに、コメントの部分を外して実行すればわかりますが、最初に全部見に良いって、終わったところまでを随時だすようになっています。順番に実行するとか、全部終わるまで待つとか、無粋なことにはなっていないはずです。

    キャンセル

  • 2017/11/20 23:09

    わわわわわ。1日も考えて頂いて大変恐縮です。このお題、決して簡単なものではないですよね。皆さん、それぞれ異なるやり方をしていてすごいです。

    キャンセル

+2

非同期でうけとって、与えた順にソートしなおしてから表示すればいいのでは?

var a=["1.txt","2.txt","3.txt","4.txt","5.txt"];
var b=[];
for(var i=0;i<a.length;i++){
  getData(i,a[i]);
};
var timerId=setInterval(function(){
  if(b.length==a.length){
    clearInterval(timerId);
    var c=b.sort(function(x,y){return x[0]>y[0];});
    for(var i=0;i<c.length;i++){
      console.log(c[i][1]);
    }
    console.log("all.done");
  }
},100);
function getData(num,url){
  var xhr = new XMLHttpRequest();
  xhr.open( "GET", url );
  xhr.onreadystatechange=function(){
    if(( xhr.readyState == 4 ) && ( xhr.status == 200 )){
      b.push([num,xhr.response]);
    }
  }
  xhr.send( '' );
}

 追記

※遅延を発生させるため、読み込むファイルはphpを指定してあります

var a=["1.php","2.php","3.php","4.php","5.php"];
var b=a.map(function(){return null});
for(var i=0;i<a.length;i++){
  getData(i,a[i]);
};
var pointer=0;
var counter=0;
var timerId=setInterval(function(){
  while(b[pointer]!==null){
    console.log(b[pointer]);
    pointer++;
    if(pointer>=b.length){
      console.log("all.done!");
      clearInterval(timerId);
      break;
    }
  }
  counter++;
  if(counter>100){
    console.log("stop!");
    clearInterval(timerId);
  } 
},100);
function getData(num,url){
  var xhr = new XMLHttpRequest();
  xhr.open( "GET", url );
  xhr.onreadystatechange=function(){
    if(( xhr.readyState == 4 ) && ( xhr.status == 200 )){
      b[num]=xhr.response;
    }
  }
  xhr.send( '' );
}
  • 1.php
<?PHP
echo 1;
?>
  • 2.php
<?PHP
sleep(3);
echo 2;
?>
  • 3.php
<?PHP
sleep(2);
echo 3;
?>
  • 4.php
<?PHP
sleep(4);
echo 4;
?>
  • 5.php
<?PHP
echo 5;
?>

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/11/20 23:07

    これだと全てを受け取った後に初めて出力する形になりませんか?Promise.allみたいな。

    キャンセル

  • 2017/11/20 23:16

    すみません、仕様がいまいち理解できません
    1から5の順で表示したいけど、非同期で同時に読みたいんですよね?
    たとえば4.txtが遅い場合、1,2,3は先に表示して、5は、またしておくってことですかね?

    キャンセル

  • 2017/11/20 23:34

    はい、その通りでございます。

    キャンセル

  • 2017/11/21 14:31 編集

    ご指定の内容に併せました
    (非同期でよみつつ、先頭から順番に読み終わったものから表示する)

    キャンセル

+2

試しに書いてみました。

(() => {
  const URLs = ['1.json', '2.json', '3.json', '4.json', '5.json'];

  const ajax = (url) => {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.send('');

    return xhr;
  };

  const proc = (responses) => {
    return () => {
      const flag = responses.every((response) => {
        return response.readyState === 4 && response.status === 200;
      });

      if (!flag) {
        setTimeout(proc(responses));
      }
      else {
        console.log('success');
        responses.forEach((response) => {
          console.log(response.responseText);
        });
      }
    };
  };

  const responses = URLs
    .map((url) => {
      return ajax(url);
    });

  proc(responses)();
})();

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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

  • JavaScript

    13876questions

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

  • Node.js

    1560questions

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

  • Ajax

    958questions

    Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

  • npm

    208questions

    npmは、Node Packaged Modulesの略。Node.jsのライブラリ・パッケージを管理できるツールです。様々なモジュールを簡単にインストールでき、自分でモジュールを作成し公開する際にも使用できます。

  • Express

    185questions

    ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。