async / await restituisce implicitamente la promessa?


111

Ho letto che le funzioni asincrone contrassegnate dalla asyncparola chiave restituiscono implicitamente una promessa:

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

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

ma questo non è coerente ... supponendo che doSomethingAsync()restituisca una promessa e la parola chiave await restituirà il valore dalla promessa, non dalla promessa itsef, quindi la mia funzione getVal dovrebbe restituire quel valore, non una promessa implicita.

Allora qual è esattamente il caso? Le funzioni contrassegnate dalla parola chiave async restituiscono implicitamente le promesse o controlliamo ciò che restituiscono?

Forse se non restituiamo esplicitamente qualcosa, restituiscono implicitamente una promessa ...?

Per essere più chiari, c'è una differenza tra quanto sopra e

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

Nella mia sinossi il comportamento è effettivamente incoerente con le dichiarazioni di ritorno tradizionali. Sembra che quando restituisci esplicitamente un valore non promesso da una asyncfunzione, lo costringerà a racchiuderlo in una promessa. Non ho un grosso problema con esso, ma sfida il normale JS.


1
Cosa console.logmostra?
Barmar

è il valore trasmesso dalla funzione di risoluzione della promessa, non la promessa stessa
Alexander Mills

Forse attendere scarta il risultato dalla promessa.
Hamlet Hakobyan

in realtà, mi sbagliavo, registra una promessa
Alexander Mills

2
Le promesse di JavaScript stanno tentando di imitare il comportamento di attesa asincrono di c #. Tuttavia, storicamente era presente molta struttura per supportarla con c # e nessuna in JavaScript. Quindi, sebbene in molti casi d'uso possa sembrare molto simile, è in qualche modo un termine improprio.
Travis J,

Risposte:


138

Il valore restituito sarà sempre una promessa. Se non restituisci esplicitamente una promessa, il valore restituito verrà automaticamente racchiuso in una promessa.

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));

Stessa cosa anche se c'è un file 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));

Promette lo scartamento automatico, quindi se restituisci una promessa per un valore dall'interno di una asyncfunzione, riceverai una promessa per il valore (non una promessa per una promessa per il valore).

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));

Nella mia sinossi il comportamento è effettivamente incoerente con le dichiarazioni di ritorno tradizionali. Sembra che quando restituisci esplicitamente un valore non promesso da una funzione asincrona, lo imporrà di racchiuderlo in una promessa. Non ho un grosso problema con esso, ma sfida il normale JS.

ES6 ha funzioni che non restituiscono esattamente lo stesso valore di return. Queste funzioni sono chiamate generatori.

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

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

// Logs 'test'.
console.log(foo().next().value);

3
"il valore restituito verrà automaticamente racchiuso in una promessa" dal metodo statico Promise.resolve, ovvero, se l'istruzione return di una funzione asincrona è - return x; diventa implicitamente - return Promise.resolve (x);
adnan2nd

È considerata cattiva pratica restituire semplicemente la promessa creata automaticamente invece di crearla esplicitamente da soli? In qualche modo mi piace l'approccio pulito in molti casi.
marlar

24

Ho dato un'occhiata alle specifiche e ho trovato le seguenti informazioni. La versione breve è che un async functiondesugars a un generatore che produce Promises. Quindi, sì, le funzioni asincrone restituiscono promesse .

Secondo la specifica tc39 , è vero quanto segue:

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

Desugars a:

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

Dove spawn"è una chiamata al seguente algoritmo":

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); });
    });
}

"La versione breve è che una funzione asincrona desugar a un generatore che produce Promesse." Penso che potresti confondere async functioncon async function*. Il primo restituisce semplicemente una promessa. Quest'ultimo restituisce un generatore che fa promesse.
cdhowie

Questa risposta è in gran parte un riferimento alle specifiche e, dopo la revisione, non credo che ci sia alcuna confusione. È vero, le funzioni asincrone restituiscono promesse, ma per fare ciò si desugarano ai generatori che producono promesse.
Jon Surrell

-1

Aggiungi solo await prima della tua funzione quando la chiami:

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

1
wait è valido solo in funzione async
Han Van Pham

-3

async non restituisce la promessa, la parola chiave await attende la risoluzione della promessa. async è una funzione di generatore migliorata e await funziona un po 'come yield

Penso che la sintassi (non sono sicuro al 100%) lo sia

async function* getVal() {...}

Le funzioni del generatore ES2016 funzionano un po 'così. Ho creato un gestore di database basato su noioso che si programma in questo modo

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}));
});

Nota come lo programma come normale sincrono, in particolare su

yield connection.execSql e a yield connection.callProcedure

La funzione db.exec è un generatore basato su Promise abbastanza tipico

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);
    });
  });
}

4
" Async è una funzione di generazione avanzata " - no, non lo è davvero.
Bergi

Come affermato sopra, le "funzioni asincrone" restituiscono effettivamente una promessa. Almeno concettualmente, il punto principale dell'istruzione "async" è racchiudere i valori di ritorno di quella funzione in una promessa. Puoi anche "attendere" su una semplice vecchia funzione che restituisce una promessa, e tutto funziona, perché "funzione asincrona" === "funzione che restituisce una promessa".
spettro

2
@bergi, in realtà, è una funzione di generatore migliorata. una funzione generatore che restituisce sempre una promessa .. o qualcosa del genere.
Alexander Mills
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.