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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Node.js

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

JavaScript

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

Q&A

解決済

1回答

4362閲覧

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

TEA

総合スコア65

Node.js

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

JavaScript

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

0グッド

1クリップ

投稿2016/06/10 08:41

編集2016/06/14 01:07

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

javascript

1 2// 配列の取得を行うタスク 3function taskA() { 4 return new Promise(function(resolve, reject) { 5 var Arr = ["a","b","c","d","e"]; 6 resolve(Arr); 7 }); 8} 9 10// taskAで取得した値を加工するタスク 11function taskB(arr) { 12 var editedArr = []; 13 return new Promise(function(resolve, reject) { 14 return arr.reduce(function(promise, value) { 15 return promise.then(function() { 16 return taskB_Work(value); 17 }).then(function(editedElement){ 18 editedArr.push(editedElement); 19 }); 20 }, Promise.resolve() ) 21 .then( 22 function(){ 23 resolve(editedArr); 24 }, 25 function(){ 26 reject("taskB 処理に失敗"); 27 } 28 ); 29 }); 30} 31 32 33// taskBのreduceで呼び出され、配列要素に対して行う処理 34function taskB_Work(value){ 35 return new Promise(function(resolve, reject) { 36 setTimeout(function () { 37 console.log("[taskB_Work] value="+value); 38 resolve(value + "_edited"); 39 }, 2000); 40 }); 41} 42 43// taskBで加工した配列の値を出力するタスク 44function taskC(editedArr) { 45 return new Promise(function(resolve, reject) { 46 console.log("[taskC]:"); 47 console.log(editedArr); 48 resolve(); 49 }); 50} 51 52 53 54//逐次処理を行う 55function sequenceTasks(tasks) { 56 function recordValue(results, value) { 57 return value; 58 } 59 var pushValue = recordValue.bind(null, []); 60 return tasks.reduce(function (promise, task) { 61 return promise.then(task).then(pushValue); 62 }, Promise.resolve()); 63} 64 65var promises = { 66 doTaskA: function() { 67 return taskA().then(); 68 }, 69 doTaskB: function(arr) { 70 return taskB(arr).then(); 71 }, 72 doTaskC: function(editedArr) { 73 return taskC(editedArr).then(); 74 } 75}; 76 77function main() { 78 return sequenceTasks([ promises.doTaskA, promises.doTaskB, promises.doTaskC ]); 79} 80 81 82main().then(function(value) { 83 console.log("complete!"); 84 //function taskB_Work(value)`で記述する処理によっては、逐次処理がここまで進むものの、プロセスが終了しない事があります。 85}).catch(function(error) { 86 console.log("error:" + error); 87});

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

  • taskA : 改行区切りのURLが保存されているテキストファイルを開き、URLを配列要素に格納
  • taskB : 各配列要素のURLに対して、flickrapiを実行
  • taskC : 新たに取得したURLをテキストファイルで出力

という流れです。

javascript

1 2// flickrのリサイズ画像URL(inPhotoURL)を元に、オリジナルサイズの画像URLを取得する。 3/* 4 var FlickrAPI = require("flickrapi"); 5 var flickrApiOptions = { 6 api_key : process.env.api_key, 7 secret : process.env.secret, 8 permissions : process.env.permissions, 9 user_id : process.env.user_id, 10 access_token : process.env.access_token, 11 access_token_secret: process.env.access_token_secret 12 }; 13*/ 14function taskB_Work(inPhotoURL){ 15 return new Promise(function(resolve, reject) { 16 FlickrAPI.authenticate(flickrApiOptions, function(error, flickr) { 17 18 var matchPat = inPhotoURL.match(/(https:\/\/farm[0-9]\.staticflickr\.com\/[0-9]+\/)([0-9]+)_([0-9a-z]+)[0-9a-z_]*\.jpg/); 19 20 var parameter = { 21 "api_key" : flickrApiOptions.api_key , 22 "photo_id" : matchPat[2], 23 "secret" : matchPat[3] 24 }; 25 26 if(!error){ 27 // getInfoにより、originalsecretとoriginalformatを取得 28 // (getInfo) https://www.flickr.com/services/api/flickr.photos.getInfo.html 29 flickr.photos.getInfo(parameter, function(err, result) { 30 if(err) { 31 reject("flickr APIの実行に失敗しました : " + err); 32 }else{ 33 resolve(matchPat[1] + matchPat[2] + "_"+ result.photo.originalsecret +"_o."+ result.photo.originalformat); 34 } 35 }); 36 }else{ 37 reject("flickr APIの認証に失敗しました :" + error); 38 } 39 }); 40 41 }); 42} 43

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

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

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

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

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

horse_n_deer

2016/06/10 23:05

関数外で状態を保持する意味はありますか?もし、module.exportsなどで関数を提供している場合、複数回の同時実行でArr_editedが壊れると思いますが…。
TEA

2016/06/13 08:04

ご指摘ありがとうございます。状態とは具体的に、配列の値のことでしょうか?関数外で状態を保持する理由は特にありません。promiseの返り値をどのように渡せばいいのか不明でした(特に関数taskB)ので、自分なりに理解できるコードを記述させて頂きました。 指摘後、配列については関数内で定義するよう変更してみました。こちらも含め、質問の内容についてご意見をいただけましたら幸いです。
horse_n_deer

2016/06/13 12:34

「プロセスが終了しない」を再現できる`taskB_Work`の内容を書くことは可能ですか?再現できない以上は、「ファイルの監視やサーバーのlistenなど、中でプロセスが落ちない処理を実行している」という予想くらいしか付かないです。
TEA

2016/06/14 00:58

`taskB_Work`の内容を追記しました。node-flickrapiを利用しているのですが、そのコールバックでreject();している事が問題なのかなと考えていますが、具体的な事が分かりません。taskB_Workは配列要素の数だけ実行され、最後にtaskCが実行されますが、プロセスが落ちない状況となっています。こちらについてご意見をいただけましたら幸いです。 また、promiseの記述方法については、一つ目で示したjavascriptコードのような記述で問題ないでしょうか?promiseに関する記述が原因でプロセスが落ちない原因となっていないかも不明でしたので、差し支えなければこの件につきましてもご意見をお待ちしております。
TEA

2016/06/17 00:43

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

回答1

0

ベストアンサー

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

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

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

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

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

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

javascript

1// 配列の取得を行うタスク 2function taskA() { 3 return ["a","b","c","d","e"]; 4} 5 6// taskAで取得した値を加工するタスク 7function taskB(arr) { 8 return arr.reduce(function(promise, value) { 9 return promise.then(function(editedArray) { 10 return taskB_Work(value).then(function(editedElement) { 11 editedArray.push(editedElement); 12 return editedArray; 13 }); 14 }); 15 }, Promise.resolve([])) 16 .catch(function() { 17 throw new Error("taskB 処理に失敗"); 18 }); 19} 20 21 22// taskBのreduceで呼び出され、配列要素に対して行う処理 23function taskB_Work(value){ 24 return new Promise(function(resolve, reject) { 25 setTimeout(function () { 26 console.log("[taskB_Work] value="+value); 27 resolve(value + "_edited"); 28 }, 2000); 29 }); 30} 31 32// taskBで加工した配列の値を出力するタスク 33function taskC(editedArr) { 34 console.log("[taskC]:"); 35 console.log(editedArr); 36}; 37 38function main() { 39 return taskB(taskA()).then(taskC); 40} 41 42main().then(function() { 43 console.log("complete!"); 44}).catch(function(error) { 45 console.log("error:" + error); 46});

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

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

投稿2016/06/24 04:36

編集2016/06/27 11:05
cither

総合スコア61

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

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

TEA

2016/06/28 07:01 編集

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問