async / await gibt implizit Versprechen zurück?

111

Ich habe gelesen, dass asynchrone Funktionen, die mit dem asyncSchlüsselwort gekennzeichnet sind, implizit ein Versprechen zurückgeben:

async function getVal(){
 return await doSomethingAync();
}

var ret = getVal();
console.log(ret);

aber das ist nicht kohärent ... vorausgesetzt , doSomethingAsync()kehrt ein Versprechen, und das await Schlüsselwort wird der Wert aus dem Versprechen , zurückzukehren, nicht das Versprechen ITSEF, dann meine getVal Funktion sollte diesen Wert zurückgeben, nicht eine implizite Versprechen.

Was genau ist also der Fall? Geben Funktionen, die mit dem Schlüsselwort async gekennzeichnet sind, implizit Versprechen zurück oder steuern wir, was sie zurückgeben?

Wenn wir etwas nicht explizit zurückgeben, geben sie vielleicht implizit ein Versprechen zurück ...?

Um klarer zu sein, gibt es einen Unterschied zwischen oben und

function doSomethingAync(charlie) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(charlie || 'yikes');
        }, 100);
    })
}

async function getVal(){
   var val = await doSomethingAync();  // val is not a promise
   console.log(val); // logs 'yikes' or whatever
   return val;  // but this returns a promise
}

var ret = getVal();
console.log(ret);  //logs a promise

In meiner Zusammenfassung ist das Verhalten in der Tat nicht mit den traditionellen return-Anweisungen vereinbar. Wenn Sie einen Nicht-Versprechen-Wert explizit von einer asyncFunktion zurückgeben, wird er anscheinend zwangsweise in ein Versprechen eingeschlossen. Ich habe kein großes Problem damit, aber es trotzt normalem JS.

Alexander Mills
quelle
1
Was console.logzeigt?
Barmar
Es ist der Wert, der von der Versprechungsauflösungsfunktion übergeben wird, nicht das Versprechen selbst
Alexander Mills
Vielleicht warten warten, um das Ergebnis von Versprechen auszupacken.
Hamlet Hakobyan
Eigentlich habe ich mich geirrt, es protokolliert ein Versprechen
Alexander Mills
2
Die Versprechen von JavaScript versuchen, das asynchrone Warteverhalten von c # nachzuahmen. In der Vergangenheit gab es jedoch eine Menge Strukturen, die dies mit c # unterstützen, und keine in JavaScript. Während es in vielen Anwendungsfällen sehr ähnlich zu sein scheint, ist es eine Art Fehlbezeichnung.
Travis J

Antworten:

135

Der Rückgabewert wird immer ein Versprechen sein. Wenn Sie ein Versprechen nicht explizit zurückgeben, wird der von Ihnen zurückgegebene Wert automatisch in ein Versprechen eingeschlossen.

async function increment(num) {
  return num + 1;
}

// Even though you returned a number, the value is
// automatically wrapped in a promise, so we call
// `then` on it to access the returned value.
//
// Logs: 4
increment(3).then(num => console.log(num));

Das Gleiche, auch wenn es eine gibt await.

function defer(callback) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve(callback());
    }, 1000);
  });
}

async function incrementTwice(num) {
  const numPlus1 = await defer(() => num + 1);
  return numPlus1 + 1;
}

// Logs: 5
incrementTwice(3).then(num => console.log(num));

Versprechen werden automatisch ausgepackt. Wenn Sie also innerhalb einer asyncFunktion ein Versprechen für einen Wert zurückgeben , erhalten Sie ein Versprechen für den Wert (kein Versprechen für ein Versprechen für den Wert).

function defer(callback) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve(callback());
    }, 1000);
  });
}

async function increment(num) {
  // It doesn't matter whether you put an `await` here.
  return defer(() => num + 1);
}

// Logs: 4
increment(3).then(num => console.log(num));

In meiner Zusammenfassung ist das Verhalten in der Tat nicht mit den traditionellen return-Anweisungen vereinbar. Wenn Sie explizit einen Nicht-Versprechen-Wert von einer asynchronen Funktion zurückgeben, wird er zwingend in ein Versprechen eingeschlossen. Ich habe kein großes Problem damit, aber es trotzt normalem JS.

ES6 verfügt über Funktionen, die nicht genau den gleichen Wert wie das zurückgeben return. Diese Funktionen werden als Generatoren bezeichnet.

function* foo() {
  return 'test';
}

// Logs an object.
console.log(foo());

// Logs 'test'.
console.log(foo().next().value);
Nathan Wall
quelle
3
"Der von Ihnen zurückgegebene Wert wird automatisch in ein Versprechen eingeschlossen" durch die statische Methode Promise.resolve, dh wenn die return-Anweisung einer asynchronen Funktion lautet - return x; es wird implizit - return Promise.resolve (x);
Adnan2nd
Wird es als schlechte Praxis angesehen, das automatisch erstellte Versprechen einfach zurückzugeben, anstatt es explizit selbst zu erstellen? Irgendwie mag ich den sauberen Ansatz in vielen Fällen.
Marlar
24

Ich habe mir die Spezifikation angesehen und die folgenden Informationen gefunden. Die kurze Version ist, dass ein async functionDesugar zu einem Generator, der Promises ergibt . Also, ja, asynchrone Funktionen geben Versprechen zurück .

Gemäß der tc39-Spezifikation gilt Folgendes:

async function <name>?<argumentlist><body>

Desugars zu:

function <name>?<argumentlist>{ return spawn(function*() <body>, this); }

Wobei spawn"der folgende Algorithmus aufgerufen wird":

function spawn(genF, self) {
    return new Promise(function(resolve, reject) {
        var gen = genF.call(self);
        function step(nextF) {
            var next;
            try {
                next = nextF();
            } catch(e) {
                // finished with failure, reject the promise
                reject(e);
                return;
            }
            if(next.done) {
                // finished with success, resolve the promise
                resolve(next.value);
                return;
            }
            // not finished, chain off the yielded promise and `step` again
            Promise.resolve(next.value).then(function(v) {
                step(function() { return gen.next(v); });
            }, function(e) {
                step(function() { return gen.throw(e); });
            });
        }
        step(function() { return gen.next(undefined); });
    });
}
Jon Surrell
quelle
"Die Kurzversion ist, dass eine asynchrone Funktion einen Generator entlädt, der Versprechen liefert." Ich denke , man kann verwirrend sein , async functionmit async function*. Ersteres gibt einfach ein Versprechen zurück. Letzterer gibt einen Generator zurück, der Versprechen gibt.
cdhowie
Diese Antwort bezieht sich größtenteils auf die Spezifikation, und nach Überprüfung glaube ich nicht, dass es Verwirrung gibt. Es ist wahr, asynchrone Funktionen geben Versprechen zurück, aber um dies zu tun, werden sie an Generatoren weitergeleitet, die Versprechen geben.
Jon Surrell
-1

Fügen Sie einfach wait vor Ihrer Funktion hinzu, wenn Sie sie aufrufen:

var ret = await  getVal();
console.log(ret);
Mohsen Gharivand
quelle
1
Warten ist nur in asynchroner Funktion gültig
Han Van Pham
-3

async gibt das Versprechen nicht zurück, das Schlüsselwort await wartet auf die Lösung des Versprechens. async ist eine erweiterte Generatorfunktion und das Warten funktioniert ein bisschen wie Yield

Ich denke, die Syntax (ich bin nicht 100% sicher) ist

async function* getVal() {...}

Die Generatorfunktionen des ES2016 funktionieren ein bisschen so. Ich habe einen Datenbank-Handler erstellt, der auf mühsam basiert und den Sie so programmieren

db.exec(function*(connection) {
  if (params.passwd1 === '') {
    let sql = 'UPDATE People SET UserName = @username WHERE ClinicianID = @clinicianid';
    let request = connection.request(sql);
    request.addParameter('username',db.TYPES.VarChar,params.username);
    request.addParameter('clinicianid',db.TYPES.Int,uid);
    yield connection.execSql();
  } else {
    if (!/^\S{4,}$/.test(params.passwd1)) {
      response.end(JSON.stringify(
        {status: false, passwd1: false,passwd2: true}
      ));
      return;
    }
    let request = connection.request('SetPassword');
    request.addParameter('userID',db.TYPES.Int,uid);
    request.addParameter('username',db.TYPES.NVarChar,params.username);
    request.addParameter('password',db.TYPES.VarChar,params.passwd1);
    yield connection.callProcedure();
  }
  response.end(JSON.stringify({status: true}));

}).catch(err => {
  logger('database',err.message);
  response.end(JSON.stringify({status: false,passwd1: false,passwd2: false}));
});

Beachten Sie, wie ich es gerade wie normal synchron programmiere, besonders bei

yield connection.execSql und bei yield connection.callProcedure

Die Funktion db.exec ist ein ziemlich typischer Promise-basierter Generator

exec(generator) {
  var self = this;
  var it;
  return new Promise((accept,reject) => {
    var myConnection;
    var onResult = lastPromiseResult => {
      var obj = it.next(lastPromiseResult);
      if (!obj.done) {
        obj.value.then(onResult,reject);
      } else {
       if (myConnection) {
          myConnection.release();
        }
        accept(obj.value);
      }
    };
    self._connection().then(connection => {
      myConnection = connection;
      it = generator(connection); //This passes it into the generator
      onResult();  //starts the generator
    }).catch(error => {
      reject(error);
    });
  });
}
akc42
quelle
4
" Async ist eine erweiterte Generatorfunktion " - nein, das ist es wirklich nicht.
Bergi
Wie oben erwähnt, geben 'asynchrone Funktionen' tatsächlich ein Versprechen zurück. Zumindest konzeptionell besteht der Hauptpunkt der 'async'-Anweisung darin, die Rückgabewerte dieser Funktion in ein Versprechen zu packen. Sie können sogar auf eine einfache alte Funktion 'warten', die ein Versprechen zurückgibt, und alles funktioniert, weil 'asynchrone Funktion' === 'Funktion, die das Versprechen zurückgibt'.
Spechter
2
@bergi, eigentlich ist es eine erweiterte Generatorfunktion. eine Generatorfunktion, die immer ein Versprechen zurückgibt .. oder so.
Alexander Mills