replaceのfunction内でawaitを使うことは出来るのか

簡単な方法はあるのか。

replaceについて

まずreplaceの決まり事

JavaScript

  1. const result = text.replace('a', () => {
  2. return 'b'
  3. })

上のコードでtext内の「a」を「b」に置換します。returnで返ったものがそのまま「a」と置換されます。

上のコードぐらいなら、replaceの第二引数を関数にする必要はありませんが、複雑な処理をしようとすると関数を使いたくなります。

やりたいこと

JavaScript

  1. const fs = require('fs').promises
  2. const result = text.replace('a', () => {
  3. const r = fs.readFile('test.txt')
  4. return r
  5. })

かなり端折っているので不自然なコードですが、replace内の関数内でpromiseを返す(ここではfs.promise)を使いたいという感じです。上のコードのままではfs.readFileは非同期処理になるので通常は「return r」で返るrはundefinedとなります。

これをしたいだけなら当然fs.readFileを関数内で読み込む必要がないのですが、「a」は固定値ではなく正規表現、「test.txt」は正規表現内の値を使って動的に変わる値と思ってください。

失敗例1

JavaScript

  1. const fs = require('fs').promises
  2. const result = text.replace('a', () => {
  3. const r = await fs.readFile('test.txt')
  4. return r
  5. })

awaitで止めれば行けそうですが、awaitはasync関数内でしか使えないのでawaitが機能しません。

失敗例2

JavaScript

  1. const fs = require('fs').promises
  2. const result = text.replace('a', async () => {
  3. const r = await fs.readFile('test.txt')
  4. return r
  5. })

じゃあ関数自体ににasyncをつければ解決しそうですが、replaceは関数の戻り値がStringである必要がありますが、asyncをつけることにより戻り値がPromise objectとなってしまうため、「a」と置換されるのは「r」ではなく「Promise object」となってしまいます。

失敗例3

JavaScript

  1. const fs = require('fs').promises
  2. const result = text.replace('a', () => {
  3. let r = ''
  4. (async () => {
  5. r = await fs.readFile('test.txt')
  6. })()
  7. return r
  8. })

困ったときの無名関数ですがこれもダメです。asyncの無名関数を別に待ってはくれないのでrには「''」が入ります。

失敗例4

JavaScript

  1. const fs = require('fs').promises
  2. const result = text.replace('a', () => {
  3. let r = ''
  4. await (async () => {
  5. r = await fs.readFile('test.txt')
  6. })()
  7. return r
  8. })

「失敗例1」と同じ理由でいけません。awaitはasync関数内でしか使えません。

失敗例5

JavaScript

  1. const fs = require('fs').promises
  2. const result = text.replace('a', async () => {
  3. let r = ''
  4. await (async () => {
  5. r = await fs.readFile('test.txt')
  6. })()
  7. return r
  8. })

「失敗例2」と同じと完全に同じ。Promiseが返ります。

失敗例6

JavaScript

  1. const fs = require('fs').promises
  2. const result = text.replace('a', () => {
  3. return (async () => {
  4. r = await fs.readFile('test.txt')
  5. return r
  6. })()
  7. })

無名関数内でreturnをすると無名関数自体の返り値となってくれるので、それを利用してみましたが、asyncが付いてしまっているので返り値がPromiseとなってしまいます。

string-replace-async

npmのパッケージに「string-replace-async」が存在し、上の問題を解決することができます。使えば一発解決しそうですが、replace単独で行おうとすると結構大掛かりなことになりそうです。「string-replace-async」自体のコードを読むと結構いろいろやっています(雑)

replaceさせる前にまずmatchで集めてからとかすれば上手くいきそうな気がしますが、やりたいことが簡単な割に複雑になりそうです。

まとめ

Promiseの中に入っている値(thenしたときにとれる値)を取れる関数とかがあれば解決するかなと思ったりしますが、今のところ無さそうなのですんなりはいかなそうです。

こんな方法で簡単にできるよ!というのがあれば教えてください。「fs.readFileSync」で解決!とかは無しでお願いします。