Async: Behandeln Sie Funktionen ohne Rückruf als Versprechen

Erstellt am 9. Juli 2019  ·  12Kommentare  ·  Quelle: caolan/async

Das aktuelle Verhalten ist sehr überraschend:

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

Denn für kurze Funktionen ist es verlockend, einfach foo: () => doSomething() zu schreiben. Das Schlüsselwort async vergisst man dort sehr leicht

Aber derzeit funktioniert dies nicht, es muss als foo: async () => doSomething()

Könnten wir lieber prüfen, ob ein Rückruf übergeben wird oder nicht, anstatt zu prüfen, ob die Funktion eine AsyncFunction ist?

wont fix

Hilfreichster Kommentar

Auch wenn dieses Problem geschlossen ist, möchte ich ab dem oben verlinkten Problem hier die @caub- Anweisungen noch einmal hervorheben.
Das aktuelle Verhalten hat mich sehr verwirrt, und obwohl ich die hier besprochenen technischen Einschränkungen (irgendwie) verstehe, glaube ich, dass die Dokumentation einfach nicht dem aktuellen Verhalten entspricht.
Bei mapLimit steht zB "Returns: a Promise, wenn kein Callback übergeben wird" - Ich habe mit der Callback-Version angefangen, dann den Callback im Grunde weggelassen und war überrascht, dass ich das Promise nicht bekomme (weil ich nicht hatte das Schlüsselwort 'async').
außerdem wird die 'callback'-Anweisung in der iteratee-Funktion dann nicht benötigt, stattdessen müssen Sie einen Wert 'zurückgeben'.
Also sollte entweder das Verhalten geändert (bevorzugt) oder zumindest Dokumentation und Beispiele aus meiner Sicht angeglichen werden, um Verwechslungen zu vermeiden.

Alle 12 Kommentare

Wir haben keine Möglichkeit, zuverlässig zu erkennen, ob eine Funktion ein Promise zurückgibt oder nicht und ob wir ihr vorher einen Callback übergeben müssen oder nicht. ( Function.length oder das Parsen von Function.toString() ist nicht gut genug) Darum geht es bei asyncify -- das Konvertieren von Promise-Returning-Funktionen in Funktionen, die einen Callback akzeptieren. Da wir async Funktionen erkennen können, da sie einen anderen Typ haben, können wir sie automatisch asyncify (so funktioniert das intern). Aber bei normalen Funktionen, die zufällig ein Promise zurückgeben, können wir dies nicht feststellen, bevor Async sie aufruft.

@aearly hmm richtig, also vielleicht etwas einfacheres wie das Erkennen des Rückrufs auf der obersten Ebene?

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
}

Wir haben schon so etwas in der Art - geben Sie ein Versprechen zurück, wenn Sie den Rückruf an async.parallel auslassen. Wir gehen auch nicht davon aus, dass die von Ihnen übergebenen Funktionen Versprechen zurückgeben, wenn Sie den Rückruf auslassen, da wir keine Möglichkeit haben, die Absicht des Benutzers anzunehmen. Vielleicht verstehe ich falsch was du meinst?

Ok, seien wir konkret: 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
    },
  })
);

Ich denke, in einer zukünftigen nächsten Hauptversion könnte es interessant sein, Callback nur in ähnlicher Weise zu unterstützen:

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

Eliminieren unnötiger async Schlüsselwörter in den inneren Funktionen

Der Vorteil ist mehr Übersichtlichkeit. Ich verstehe Ihren Standpunkt, dass Sie keine Benutzerabsicht annehmen, aber Sie gehen immer noch davon aus, dass die Leute das Schlüsselwort async nicht vergessen, wenn sie die Promise-Auflösung verwenden möchten

Das würde die Abwärtskompatibilität ziemlich stark beeinträchtigen – zum Beispiel könnten Sie Dinge wie Async.map(fileNames, fs.readFile) nicht mehr tun.

Könnten Sie erklären, wo es brechen würde?

Was derzeit kaputt ist:

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

Die Idee ist, das dritte zu reparieren

Ich bekomme diese Probleme ständig, zum Beispiel habe ich geschrieben:

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

irgendwo, und wieder, Timeout, weil mir ein async item => item.toDoc.. fehlte

Wenn ich in diesem Fall alleine bin, lohnt es sich nicht, aber wenn mehr Leute in dieser Situation sind, denke ich, dass es sich lohnt, die vorgeschlagene Änderung in Betracht zu ziehen

Dieses Beispiel mit async.map ist auch sehr repräsentativ, da Sie meinen Punkt mit dem Wie sehen können

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

funktioniert ohne async item => item.toDoc..

Es gibt keine Möglichkeit, den 3. zuverlässig zu beheben, dies wurde in der Vergangenheit viel diskutiert. Wir können den Rückgabewert einer Funktion nicht überprüfen und dann rückwirkend keinen Callback an die Funktion übergeben. Das Schlüsselwort async ist so gut wie es nur geht.

@caub Shameless Plug hier, aber ich denke, dass asyncp in der Lage ist, das zu verarbeiten, was Sie hier suchen.

Auch wenn dieses Problem geschlossen ist, möchte ich ab dem oben verlinkten Problem hier die @caub- Anweisungen noch einmal hervorheben.
Das aktuelle Verhalten hat mich sehr verwirrt, und obwohl ich die hier besprochenen technischen Einschränkungen (irgendwie) verstehe, glaube ich, dass die Dokumentation einfach nicht dem aktuellen Verhalten entspricht.
Bei mapLimit steht zB "Returns: a Promise, wenn kein Callback übergeben wird" - Ich habe mit der Callback-Version angefangen, dann den Callback im Grunde weggelassen und war überrascht, dass ich das Promise nicht bekomme (weil ich nicht hatte das Schlüsselwort 'async').
außerdem wird die 'callback'-Anweisung in der iteratee-Funktion dann nicht benötigt, stattdessen müssen Sie einen Wert 'zurückgeben'.
Also sollte entweder das Verhalten geändert (bevorzugt) oder zumindest Dokumentation und Beispiele aus meiner Sicht angeglichen werden, um Verwechslungen zu vermeiden.

@caub Das ist der Grund
https://github.com/caolan/async/blob/8aecf108b3922bc5211036706a0f6f75e02bd42b/lib/internal/wrapAsync.js#L3 :L5

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

Auf diese Weise ist es einfacher zu überprüfen, ob die Funktion ein Promise zurückgibt, ohne sie tatsächlich aufzurufen. Aber stimme dir trotzdem zu, dass dies gegen die Async/Await-Konventionen verstößt. Da das Hinzufügen eines async Schlüsselworts ohne ein internes await in den meisten Fällen als schlechte Vorgehensweise angesehen wird.

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

Den Hauptteil einer async Funktion kann man sich so vorstellen, als würde er durch null oder mehr Wait-Ausdrücke geteilt. Der Code der obersten Ebene wird bis einschließlich des ersten Wait-Ausdrucks (sofern vorhanden) synchron ausgeführt.

... und daher ist es nutzlos und am schlimmsten wird es etwas Performance-Overhead verursachen, wenn der Code beispielsweise von babel transpiliert wird, da mehr Code und Schritte hinzugefügt werden, nur weil das Schlüsselwort async

Zum Beispiel die folgenden:

async function foo() {
   return 1
}

...ist äquivalent zu:

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

aber mit dieser Bibliothek

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

und

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

sind nicht gleichwertig!

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen