Async: コールバックなしで関数を約束どおりに扱う

作成日 2019年07月09日  ·  12コメント  ·  ソース: caolan/async

現在の動作は非常に驚くべきものです。

await async.parallel({foo: async()=>Promise.all([1,2])})
// { foo: [ 1, 2 ] }
await async.parallel({foo: ()=>Promise.all([1,2])})
// waits forever because non-async function expects a ballback

短い関数の場合、 foo: () => doSomething()書くだけで魅力的です。 そこでasyncキーワードを忘れるのはとても簡単です

しかし、現在これは機能しません。 foo: async () => doSomething()と書く必要があります

関数がAsyncFunctionであるかどうかをチェックする代わりに、コールバックが渡されたかどうかをチェックできますか?

wont fix

最も参考になるコメント

この問題が解決されたとしても、上記のリンクされた問題の時点​​で、ここで@caubステートメントを再度強調したいと思います。
現在の動作は私を大いに混乱させました。私は(一種の)ここで説明した技術的な制限を理解していますが、ドキュメントは単に現在の動作と一致していないと思います。
たとえば、mapLimitの場合、「リターン:コールバックが渡されない場合は、promise」と表示されます-コールバックバージョンから始めて、基本的にコールバックを省略し、Promiseが取得されないことに驚きました( 'async'キーワード)。
また、iteratee関数の「callback」ステートメントは必要ありません。代わりに、値を「返す」必要があります。
したがって、混乱を避けるために、動作を変更する(推奨)か、少なくともドキュメントと例を私の観点で調整する必要があります。

全てのコメント12件

関数がpromiseを返すかどうか、および関数にコールバックを渡す必要があるかどうかを事前に確実に検出する方法はありません。 ( Function.lengthまたはFunction.toString()解析では不十分です)これがasyncify目的です-promiseを返す関数をコールバックを受け取る関数に変換します。 async関数はタイプが異なるため検出できるため、自動的にasyncifyできます(これが内部でどのように機能するかを示しています)。 しかし、たまたまpromiseを返す通常の関数の場合、Asyncがそれらを呼び出す前にそれを知る方法はありません。

@aearlyうーん、

async function parallel(o, cb) {
  if (!cb) { // promise behavior
    return Object.fromEntries(
      await Promise.all(Object.entries(o).map(async ([k, v]) => [k, await v()]))
    )
  }
  // else do the callback behavior
}

私たちはすでにそのようなものです- async.parallelへのコールバックを省略した場合はpromiseを返します。 コールバックを省略した場合でも、ユーザーの意図を想定する方法がないため、渡された関数がpromiseを返すとは想定していません。 多分私はあなたが何を意味するのか誤解していますか?

わかりました、具体的にしましょう: https ://repl.it/@caub/async -async

// npm.im/async expects all inner functions to be async,
// even when using no outer callback (as 2nd arg of async.parallel)
console.log(
  await async.parallel({
    foo: async () => delay(1000).then(() => 1),
    bar: async (rs) => {
      return 2
    },
  })
);

// Doesn't work without those async,
// but I'd like this to resolve just like above instead of freezing
console.log(
  await async.parallel({
    foo: () => delay(1000).then(() => 1),
    bar: () => {
      return 2
    },
  })
);

将来の次のメジャーバージョンでは、次のような方法でのみコールバックをサポートすることが興味深いと思います。

await Promise.all([
  { then(resolve){ setTimeout(resolve, 50, 1) } }, // callback way
  2,
  delay(45).then(())=>3)
])
// [1, 2, 3]

内部関数で不要なasyncキーワードの必要性を排除します

利点は、より明確です。 ユーザーの意図を想定しないことについてのあなたのポイントは理解していますが、Promiseの解決を使用したいときに人々がasyncキーワードを忘れないだろうとあなたはまだ想定しています

これにより、下位互換性がかなり大きく損なわれます。たとえば、 Async.map(fileNames, fs.readFile)ようなことはもうできません。

どこで壊れるか説明してもらえますか?

現在壊れているのは:

await async.map(['server.js', 'package.json'], fs.readFile) // works
await async.map(['server.js', 'package.json'], fs.promises.readFile) // works
await async.map(['server.js', 'package.json'], s => fs.promises.readFile(s)) // doesn't work
await async.map(['server.js', 'package.json'], async s => fs.promises.readFile(s)) // works

アイデアは3番目のものを修正することです

私はそれらの問題を絶えず受けます、例えば私は書いた:

  // ...
  doc = await async.map(items, item => item.toDoc(currentUser, options));

async item => item.toDoc..が見つからなかったため、どこかでタイムアウトしました

その場合、私が一人なら価値はありませんが、もっと多くの人がこの状況にあるのであれば、提案された変更を検討する価値があると思います

async.mapを使用したこの例も非常に代表的なものです。これは、私のポイントをどのように見ることができるかを示しています。

  doc = await Promise.all(items.map(item => item.toDoc(currentUser, options)));

async item => item.toDoc..必要とせずに動作します

3番目のものを確実に修正する方法はありません。これは過去に多くの議論がありました。 関数の戻り値を確認してから、遡及的に関数にコールバックを渡さないようにすることはできません。 asyncキーワードは

@caub恥知らずなプラグインはここにありますが、 asyncpは、ここで探しているものを処理する機能があると思います。

この問題が解決されたとしても、上記のリンクされた問題の時点​​で、ここで@caubステートメントを再度強調したいと思います。
現在の動作は私を大いに混乱させました。私は(一種の)ここで説明した技術的な制限を理解していますが、ドキュメントは単に現在の動作と一致していないと思います。
たとえば、mapLimitの場合、「リターン:コールバックが渡されない場合は、promise」と表示されます-コールバックバージョンから始めて、基本的にコールバックを省略し、Promiseが取得されないことに驚きました( 'async'キーワード)。
また、iteratee関数の「callback」ステートメントは必要ありません。代わりに、値を「返す」必要があります。
したがって、混乱を避けるために、動作を変更する(推奨)か、少なくともドキュメントと例を私の観点で調整する必要があります。

@caubこれが理由です
https://github.com/caolan/async/blob/8aecf108b3922bc5211036706a0f6f75e02bd42b/lib/internal/wrapAsync.js#L3:L5

function isAsync(fn) {
    return fn[Symbol.toStringTag] === 'AsyncFunction';
}

このように、関数が実際に呼び出さなくてもpromiseが返されるかどうかを確認する方が簡単です。 ただし、これはasync / awaitの規則に反することに同意してください。 内部awaitなしでasyncキーワードを追加することは、ほとんどの場合、実際には悪い習慣と見なされます。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_functionから

async関数の本体は、0個以上のawait式で分割されていると考えることができます。 最初のawait式(存在する場合)までの最上位コードは、同期的に実行されます。

...したがって、コードがbabelによってトランスパイルされた場合、たとえばasyncキーワードが存在するという理由だけでコードとステップが追加されるため、パフォーマンスのオーバーヘッドがいくらか追加されます。

たとえば、次のようになります。

async function foo() {
   return 1
}

...と同等です:

function foo() {
   return Promise.resolve(1)
}

しかし、このライブラリでは、

async.mapLimit([1], 1, async v => v)

async.mapLimit([1], 1, v => Promise.resolve(v)))

同等ではありません!

このページは役に立ちましたか?
0 / 5 - 0 評価