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

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

ただいまの
回答率

90.40%

  • JavaScript

    21469questions

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

  • Node.js

    2460questions

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

(Promise)直列処理の中でreduceを扱う事はできますか?

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,752

TEA

score 58

promise初心者です。promiseの記述方法がこれで正しいか分からなかったのでご質問させてください。環境はnode.js v4.3です。

promiseの直列処理のいくつかの処理の中で、配列要素を処理するreduceを追加することはできますか?また、2つ目の質問ですが、下記コード中のfunction taskB_Work(value)で記述する処理によっては、逐次処理が最後まで進むものの、プロセスが終了しない事がありました。これはどのような原因が考えられますでしょうか?(console.log("complete!");以下を参照)

主にこのような処理を逐次処理で行いたいと考えております。

  1. 配列を取得
  2. 配列要素を元に処理 (配列要素の数だけ逐次処理したい)
  3. 処理した結果を出力

現在、以下のようなコードを書いていますが、このような形でpromiseの記述は問題ないでしょうか?

参考記事: Qiita | Promiseについて0から勉強してみた
http://qiita.com/toshihirock/items/e49b66f8685a8510bd76

// 配列の取得を行うタスク
function taskA() {
  return new Promise(function(resolve, reject) {
      var Arr = ["a","b","c","d","e"];
        resolve(Arr);
    });
}

// taskAで取得した値を加工するタスク
function taskB(arr) {
  var editedArr = [];
  return new Promise(function(resolve, reject) {
    return arr.reduce(function(promise, value) {
          return promise.then(function() {
              return taskB_Work(value);
          }).then(function(editedElement){
        editedArr.push(editedElement);
      });
      }, Promise.resolve() )
      .then(
          function(){
              resolve(editedArr);
          },
          function(){
              reject("taskB 処理に失敗");
          }
      );
  });
}


// taskBのreduceで呼び出され、配列要素に対して行う処理
function taskB_Work(value){
    return new Promise(function(resolve, reject) {
        setTimeout(function () {
              console.log("[taskB_Work] value="+value);
              resolve(value + "_edited");
        }, 2000);
    });
}

// taskBで加工した配列の値を出力するタスク
function taskC(editedArr) {
  return new Promise(function(resolve, reject) {
      console.log("[taskC]:");
      console.log(editedArr);
    resolve();
  });
}



//逐次処理を行う
function sequenceTasks(tasks) {
    function recordValue(results, value) {
        return value;
    }
    var pushValue = recordValue.bind(null, []);
    return tasks.reduce(function (promise, task) {
        return promise.then(task).then(pushValue);
    }, Promise.resolve());
}

var promises = {
  doTaskA: function() {
    return taskA().then();
  },
  doTaskB: function(arr) {
    return taskB(arr).then();
  },
  doTaskC: function(editedArr) {
    return taskC(editedArr).then();
  }
};

function main() {
 return sequenceTasks([ promises.doTaskA, promises.doTaskB, promises.doTaskC ]);
}


main().then(function(value) {
  console.log("complete!");
  //function taskB_Work(value)`で記述する処理によっては、逐次処理がここまで進むものの、プロセスが終了しない事があります。
}).catch(function(error) {
  console.log("error:" + error);
});

taskB_Workの処理内容は実際には以下のようなものを考えています。全体の処理の概要は、

  • taskA : 改行区切りのURLが保存されているテキストファイルを開き、URLを配列要素に格納
  • taskB : 各配列要素のURLに対して、flickrapiを実行
  • taskC : 新たに取得したURLをテキストファイルで出力
    という流れです。
// flickrのリサイズ画像URL(inPhotoURL)を元に、オリジナルサイズの画像URLを取得する。
/*
    var FlickrAPI = require("flickrapi");
    var flickrApiOptions = {
                api_key            : process.env.api_key,
                secret             : process.env.secret,
                permissions        : process.env.permissions,
                user_id            : process.env.user_id,
                access_token       : process.env.access_token,
                access_token_secret: process.env.access_token_secret
            };
*/
function taskB_Work(inPhotoURL){
    return new Promise(function(resolve, reject) {
        FlickrAPI.authenticate(flickrApiOptions, function(error, flickr) {

            var matchPat = inPhotoURL.match(/(https:\/\/farm[0-9]\.staticflickr\.com\/[0-9]+\/)([0-9]+)_([0-9a-z]+)[0-9a-z_]*\.jpg/);

            var parameter = {
                "api_key" : flickrApiOptions.api_key ,
                "photo_id" : matchPat[2],
                "secret" : matchPat[3]
            };

            if(!error){
                // getInfoにより、originalsecretとoriginalformatを取得
                // (getInfo) https://www.flickr.com/services/api/flickr.photos.getInfo.html
                flickr.photos.getInfo(parameter, function(err, result) {
                    if(err) {
                          reject("flickr APIの実行に失敗しました : " + err);
                    }else{
                        resolve(matchPat[1] + matchPat[2] + "_"+ result.photo.originalsecret +"_o."+ result.photo.originalformat);
                    }
                });
            }else{
                reject("flickr APIの認証に失敗しました :" + error);
            }
        });

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • TEA

    2016/06/14 09:58

    `taskB_Work`の内容を追記しました。node-flickrapiを利用しているのですが、そのコールバックでreject();している事が問題なのかなと考えていますが、具体的な事が分かりません。taskB_Workは配列要素の数だけ実行され、最後にtaskCが実行されますが、プロセスが落ちない状況となっています。こちらについてご意見をいただけましたら幸いです。

    また、promiseの記述方法については、一つ目で示したjavascriptコードのような記述で問題ないでしょうか?promiseに関する記述が原因でプロセスが落ちない原因となっていないかも不明でしたので、差し支えなければこの件につきましてもご意見をお待ちしております。

    キャンセル

  • horse_n_deer

    2016/06/15 19:09

    念のため、catchできていないPromiseがないか https://nodejs.org/api/process.html#process_event_unhandledrejection かbluebirdで確認してください。

    キャンセル

  • TEA

    2016/06/17 09:43

    ご返事いただきありがとうございます。npm install bluebirdとvar Promise = require('bluebird');した後で、process.on('unhandledRejection',.. を追加してみましたが、何かのエラーがcatchされる気配はありませんでした。。。

    キャンセル

回答 1

checkベストアンサー

0

プロセスが終了しない件については再現ができませんでしたので、Promiseの記述方法について回答してみます。

このような形でpromiseの記述は問題ないでしょうか?

同期処理をわざわざ非同期処理に変換しているため無用に複雑なコードになっているという印象を持ちました。また、PromiseをさらにPromiseで囲むような冗長な処理も回避可能です。

promiseの直列処理のいくつかの処理の中で、配列要素を処理するreduceを追加することはできますか?

タイトルにも同様のことが書かれていますが、この質問の意図が分かりませんでした。コードを見ていただくのが早いかもしれません。

ご提示のコードから不要な非同期処理を省いて私なりに書き直してみました。

// 配列の取得を行うタスク
function taskA() {
  return  ["a","b","c","d","e"];
}

// taskAで取得した値を加工するタスク
function taskB(arr) {
  return arr.reduce(function(promise, value) {
    return promise.then(function(editedArray) {
      return taskB_Work(value).then(function(editedElement) {
        editedArray.push(editedElement);
        return editedArray;
      });
    });
  }, Promise.resolve([]))
  .catch(function() {
    throw new Error("taskB 処理に失敗");
  });
}


// taskBのreduceで呼び出され、配列要素に対して行う処理
function taskB_Work(value){
  return new Promise(function(resolve, reject) {
    setTimeout(function () {
      console.log("[taskB_Work] value="+value);
      resolve(value + "_edited");
    }, 2000);
  });
}

// taskBで加工した配列の値を出力するタスク
function taskC(editedArr) {
  console.log("[taskC]:");
  console.log(editedArr);
};

function main() {
  return taskB(taskA()).then(taskC);
}

main().then(function() {
  console.log("complete!");
}).catch(function(error) {
  console.log("error:" + error);
});

then()catch()の中で同期例外を投げるとそのPromiseはreject()されたことになるので、taskBの例外処理ではそれを利用しています。Promiseの例外処理についてはこの記事が詳しいです。

ところで、Babelなどのトランスパイラを使えばasync,awaitというPromiseよりも強力な非同期処理の仕組みが利用できますのでそちらを使ってみるのも良いかもしれません。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/06/28 15:51 編集

    教えて頂きましてありがとうございました。かなりすっきりとしたコードで驚いております。一つ一つ比較して、参考にさせていただきたいと思います。async,awaitについては恥しながら初耳でした(^-^;) 学ぶ切欠を頂き感謝です。ECMAScript 2015 について下記で今後勉強したいと思います。また別の質問にてご指導いただければ幸いです。http://qiita.com/kotaroito/items/0f04cd089e6c13c79c09#_reference-57abe407b431b160dfda

    キャンセル

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

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

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

  • JavaScript

    21469questions

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

  • Node.js

    2460questions

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