javascript - returnerer asynkron funksjon implisitt et løfte?


Jeg leste at asynkrone funksjoner merket med async-nøkkelordet implisitt returnerer et løfte:

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

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

men det er ikke sammenhengende... forutsatt at doSomethingAsync() returnerer et løfte, og at nøkkelordet await vil returnere verdien fra løftet, ikke løftet selv, så burde funksjonen min getVal returnere den verdien, ikke et implisitt løfte.

Så hva er egentlig saken? Returnerer funksjoner merket med async-nøkkelordet implisitt promises, eller kontrollerer vi hva de returnerer?

Kanskje hvis vi ikke eksplisitt returnerer noe, så returnerer de implisitt et løfte...?

For å være tydeligere, det er en forskjell mellom det ovennevnte og

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

I min synopsis er oppførselen faktisk inkonsekvent med tradisjonelle return-setninger. Det ser ut til at når du eksplisitt returnerer en ikke-løfteverdi fra en async-funksjon, vil den bli tvunget innpakket i et løfte. Jeg har ikke et stort problem med det, men det bryter med normal JS.

Svar #1

Returverdien vil alltid være et løfte. Hvis du ikke eksplisitt returnerer et løfte, vil verdien du returnerer automatisk bli pakket inn i et løfte.

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

Det samme selv om det ikke er noen retur! (Promise { undefined } blir returnert)

async function increment(num) {}

Det samme selv om det er en 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));

Løfter automatisk oppheves, så hvis du returnerer et løfte om en verdi fra en async-funksjon, vil du motta et løfte om verdien (ikke et løfte om et løfte om verdien).

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

I min synopsis er oppførselen faktisk inkonsekvent med tradisjonelle return-setninger. Det ser ut til at når du eksplisitt returnerer en verdi som ikke er et løfte fra en asynkron funksjon, vil den bli tvunget innpakket i et løfte. Jeg har ikke et stort problem med det, men det bryter med normal JS.

ES6 har funksjoner som ikke returnerer nøyaktig den samme verdien som return. Disse funksjonene kalles generatorer.

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

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

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

Svar #2

Ja, en asynkron funksjon vil alltid returnere et løfte.

I henhold til tc39-spesifikasjonen desugares en async function til en generator som gir Promises.

Spesifikt:

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

Brytes ned til:

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

Hvor spawn "er et kall til følgende algoritme":

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

Svar #3

Ditt spørsmål er: Hvis jeg lager en async-funksjon, bør den returnere et løfte eller ikke? Svar: bare gjør hva du vil, og Javascript ordner det for deg.

Anta at doSomethingAsync er en funksjon som returnerer et løfte. Da

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

er helt det samme som

async function getVal(){
    return doSomethingAsync();
}

Du tenker sikkert «WTF, hvordan kan dette være det samme?», og du har rett. async vil magisk pakke en verdi inn i en Promise om nødvendig.

Enda merkeligere er det at doSomethingAsync kan skrives slik at den noen ganger returnerer et løfte og noen ganger IKKE returnerer et løfte. Likevel er begge funksjonene helt like, fordi await også er magisk. Den vil åpne et løfte om nødvendig, men det vil ikke påvirke ting som ikke er løfter.

Relaterte artikler