2016-01-12 5 views
7

Ich versuche besser zu verstehen, was ein async function in JavaScript technisch ist, auch wenn ich grundsätzlich weiß, wie man sie benutzt.technischer Unterschied zwischen ES7 Async-Funktion und ein Versprechen?

Viele Einführungen in async/await machen glauben, dass eine async Funktion im Grunde nur ein Versprechen ist, aber das ist offensichtlich nicht der Fall ist (zumindest nicht mit Babel6-transpiled code):

async function asyncFunc() { 
    // nop 
} 

var fooPromise = new Promise(r => setTimeout(r, 1)); 

console.clear(); 

console.log("typeof asyncFunc is", typeof asyncFunc); // function 
console.log("typeof asyncFunc.next is", typeof asyncFunc.next); // undefined 
console.log("typeof asyncFunc.then is", typeof asyncFunc.then); // undefined 

console.log("typeof fooPromise is", typeof fooPromise); // object 
console.log("typeof fooPromise.next is", typeof fooPromise.next); // undefined 
console.log("typeof fooPromise.then is", typeof fooPromise.then); // function 

Dennoch ist es durchaus möglich, zu await ein Versprechen, wie await fooPromise().

  • Ist ein async funtion etwas von seinen eigenen und await ist einfach kompatibel mit Versprechungen?

  • und gibt es eine Möglichkeit, zwischen einer einfachen function und einer async function zur Laufzeit zu unterscheiden (in einer Babel-kompatiblen Weise)?

Antwort

9

Ein Asynchron-Funktion ist eine Funktion, die ein Versprechen zurückgibt. Es hilft Ihnen, mit Fällen, in denen Sie eine Reihe von asynchronen Aktionen haben geschieht nacheinander:

function asyncFunc() { 
    return doSomethingAsync() // doSomethingAsync() returns a promise 
    .then(() => { 
     // do some stuff 
     return doSomethingElseAsync(); // returns a promise 
    }) 
    .then(something => { 
     // do some stuff 
     return doSomethingElseEntirelyAsync(something); // returns a promise 
    }); 
} 

wendet sich an

async function asyncFunc() { 
    await doSomethingAsync(); // awaits for a promise 
    // do some stuff 
    let something = await doSomethingElseAsync(); // awaits for a promise 
    // do some stuff 
    return doSomethingElseEntirelyAsync(something); // returns the final promise 
    // Note that even if you return a value, like return 5, the function as a whole 
    // still returns a promise! 
} 

es viel besser liest, und Sie können die üblichen Werkzeuge verwenden wie versuchen/catch und for loops, um mit ihnen zu arbeiten, obwohl sie asynchron sind.

Async-Funktionen sind NICHT ein Ersatz für Versprechungen, sie sind Zucker oben drauf, um bestimmte Fälle zu behandeln, in denen Sie viele sequentielle asynchrone Aktionen haben.

Da await ist im Grunde nur „erwarten für dieses Versprechen“, können Sie immer noch die kühlen Aggregationsmethoden wie Promise.all() verwenden und Promise.race() und warten auf das Ergebnis von mehreren (oder die erste von mehreren) verspricht.

Ich bin nicht mit einer Art der Unterscheidung der beiden in der Laufzeit vertraut, weil asynchrone Funktionen wie Klassen nur Zucker auf Promises sind. (Obwohl es Hacks geben mag, wie die .toString der Funktion zu verwenden und die Ergebnisse zu analysieren, zähle ich diese nicht).

+0

Danke, macht viel Sinn. Im Wesentlichen ist 'asyncFunc' keine Promise, aber' asyncFunc() 'ist - oder mit anderen Worten:' typeof asyncFunc(). Then == "function" ' –

+0

Das ist genau richtig. –

0

Das Paar async/await ist ein Mechanismus, mit dem Sie asynchronen Code in einem synchronen Stil schreiben können und meiner bescheidenen Meinung nach ist es die einfachste und lesbarste Syntax, die bisher mit asynchronem Code zu tun hat (siehe auch this article). Die Kraft der Syntax liegt wirklich darin, wie die await funktioniert. Um jedoch await innerhalb des Rumpfes einer Funktion zu verwenden, muss der Funktion das Präfix async vorangestellt werden.

Wenn Sie weitere Informationen benötigen, gibt es eine spec for async/await here. Die aktuelle Implementierung in Babel 5 basiert auf der https://github.com/facebook/regenerator.Wie Sie in der transpiled code die Funktion zu sehen ist zusammengestellt:

function asyncFunc(which, one, two) { 
    return regeneratorRuntime.async(function asyncFuncMaybe$(context$1$0) { 
... 

Wenn Sie babel-regenerator-runtime in Babels Paket graben Sie Facebook-Code finden. Bei line 205 Sie finden:

// Note that simple async functions are implemented on top of 
// AsyncIterator objects; they just return a Promise for the value of 
// the final result produced by the iterator. 
runtime.async = function(innerFn, outerFn, self, tryLocsList) { 
... 

Um transpile die async/await Babel Bedürfnisse ES5 tun Sie den Code neu zu ordnen, so können wir verfolgen, wo wir während der Ausführung der Funktion sind und die AsyncIterator ist das Objekt, das Kurs zu halten von diesem Staat.

Babel 6 gibt Ihnen mehr Optionen und lässt Sie die Implementierung auswählen, die Sie verwenden möchten. Siehe Transpile Async Await proposal with Babel.js?

So in Bezug auf Ihre Fragen:

  • async/await beide sind eine Sache von sich geben. Laut Spezifikation müssen sie mit Versprechen arbeiten. Insbesondere können Sie await auf ein Versprechen und wenn Sie eine async Funktion ausführen, gibt es Ihnen ein Versprechen.
  • Da eine async-Funktion transpiliert wird, um eine Antwort zu versprechen, gibt es keine direkte Möglichkeit, sie von einer asynchronen Funktion zu unterscheiden, die eine Zusage zurückgibt. Ihr fooPromise sollte eher wie var fooPromiseFunc = function() {return new Promise(r => setTimeout(r, 1))}; aussehen, die fooPromiseFunc und asyncFunc nicht von einer Black-Box prospektiv zu unterscheiden. Sie sind beide Funktionen, die ein Versprechen zurückgeben. Aus welchem ​​Grund möchten Sie zur Laufzeit eine async- und eine nicht-asynchrone Funktion unterscheiden? In der Praxis können sie auf die gleiche Weise benutzt werden, also sehe ich nicht, warum Sie sie anders bedrohen müssen. Für Debug-Zwecke, wenn Sie wirklich herausfinden müssen, ob eine Funktion async definiert ist, könnten Sie in Babel 5 etwas wie (asyncFunc+"").indexOf('regeneratorRuntime.async') > 0 oder einen genaueren regulären Ausdruck verwenden. Aber das ist wirklich Hacky und ich würde nicht in einem Kontext außerhalb des Debuggens oder Studierens verwenden.
+0

Danke für die ausführliche Erklärung. Re: * was ist der Grund, warum Sie eine async und no-async-Funktion zur Laufzeit unterscheiden möchten? *: Eigentlich wollte ich nur normale Callbacks von Promises unterscheiden (am Ende zu 'express' Promise/async-kompatibel in einem automatischer Weg). Nun, da ich weiß, wie die Dinge funktionieren, ist mir alles klar und ich konnte tatsächlich einen perfekt funktionierenden (experimentellen) Patch für 'express' erstellen. –

+0

Sie können diese 2 Links nützlich finden: https://strongloop.com/strongblog/async-error-handling-expressjs-es7- promises-generators/ und https://github.com/luin/express-promise –

+0

Danke für diese Links. Ich kannte sie bereits, bevor ich diese Frage posten konnte und mit Hilfe der Antworten fand ich einen Weg, um die Verwendung einer expliziten Wrapper-Funktion zu vermeiden und dennoch genau das gleiche Ergebnis zu erhalten. –