async / await secara implisit mengembalikan janji?

111

Saya membaca bahwa fungsi asinkron yang ditandai dengan asynckata kunci secara implisit mengembalikan sebuah janji:

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

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

tapi itu tidak koheren ... dengan asumsi doSomethingAsync()mengembalikan sebuah janji, dan kata kunci await akan mengembalikan nilai dari janji, bukan dari janji itsef, maka fungsi getVal saya harus mengembalikan nilai itu, bukan janji implisit.

Jadi apa sebenarnya masalahnya? Apakah fungsi yang ditandai oleh kata kunci async secara implisit mengembalikan promise atau kita mengontrol apa yang dikembalikan?

Mungkin jika kita tidak secara eksplisit mengembalikan sesuatu, maka mereka secara implisit mengembalikan janji ...?

Untuk lebih jelasnya, ada perbedaan antara di atas dan

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

Dalam sinopsis saya, perilaku tersebut memang tidak konsisten dengan pernyataan pengembalian tradisional. Tampaknya saat Anda secara eksplisit mengembalikan nilai non-promise dari suatu asyncfungsi, ini akan memaksa membungkusnya dalam sebuah promise. Saya tidak punya masalah besar dengan itu, tapi itu menentang JS normal.

Alexander Mills
sumber
1
Apa yang console.logdiperlihatkan?
Barmar
ini adalah nilai yang diteruskan oleh fungsi penyelesaian janji, bukan janji itu sendiri
Alexander Mills
Mungkin menunggu hasil dari janji terbuka.
Hamlet Hakobyan
sebenarnya, saya salah, itu membuat janji
Alexander Mills
2
Promise JavaScript mencoba meniru perilaku async await c #. Namun, ada banyak struktur di tempat secara historis untuk mendukungnya dengan c #, dan tidak ada di JavaScript. Jadi meskipun dalam banyak kasus penggunaan ini mungkin tampak sangat mirip, ini agak keliru.
Travis J

Jawaban:

138

Nilai pengembalian akan selalu menjadi janji. Jika Anda tidak secara eksplisit mengembalikan janji, nilai yang Anda kembalikan secara otomatis akan digabungkan dalam sebuah janji.

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

Hal yang sama meskipun ada 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));

Promises auto-unwrap, jadi jika Anda mengembalikan promise untuk nilai dari dalam suatu asyncfungsi, Anda akan menerima promise untuk nilainya (bukan promise untuk promise untuk nilainya).

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

Dalam sinopsis saya, perilaku tersebut memang tidak konsisten dengan pernyataan pengembalian tradisional. Tampaknya saat Anda mengembalikan nilai non-promise secara eksplisit dari fungsi async, ini akan memaksa membungkusnya dalam sebuah promise. Saya tidak punya masalah besar dengan itu, tapi itu menentang JS normal.

ES6 memiliki fungsi yang tidak mengembalikan nilai yang sama persis dengan return. Fungsi-fungsi ini disebut generator.

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

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

// Logs 'test'.
console.log(foo().next().value);
Nathan Wall
sumber
3
"nilai yang Anda kembalikan secara otomatis akan dibungkus dengan janji" dengan metode statis Promise.resolve, yaitu, jika pernyataan kembalian dari fungsi asinkron adalah - return x; itu secara implisit menjadi - return Promise.resolve (x);
adnan2
Apakah dianggap praktik buruk dengan hanya mengembalikan janji yang dibuat secara otomatis alih-alih membuatnya sendiri secara eksplisit? Entah bagaimana saya menyukai pendekatan bersih dalam banyak kasus.
marlar
24

Saya melihat spesifikasi dan menemukan informasi berikut. Versi singkatnya adalah async functiondesugars ke generator yang menghasilkan Promises. Jadi, ya, fungsi asinkron mengembalikan janji .

Menurut spesifikasi tc39 , berikut ini adalah benar:

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

Desugars untuk:

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

Di mana spawn"adalah panggilan ke algoritme berikut":

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
sumber
"Versi singkatnya adalah bahwa fungsi asinkron mendeskripsikan generator yang menghasilkan Promises." Saya pikir Anda mungkin bingung async functiondengan async function*. Yang pertama hanya membalas janji. Yang terakhir mengembalikan generator yang menghasilkan promise.
cdhowie
Jawaban ini sebagian besar mengacu pada spesifikasi dan, setelah ditinjau, saya tidak yakin ada kebingungan. Memang benar, fungsi async mengembalikan promise, tetapi untuk melakukan ini, fungsi tersebut memilih generator yang menghasilkan promise.
Jon Surrell
-1

Cukup tambahkan await before function Anda saat Anda memanggilnya:

var ret = await  getVal();
console.log(ret);
mohsen gharivand
sumber
1
menunggu hanya berlaku dalam fungsi async
Han Van Pham
-3

async tidak mengembalikan promise, kata kunci await menunggu penyelesaian promise. async adalah fungsi generator yang disempurnakan dan menunggu berfungsi seperti hasil

Saya pikir sintaksnya (saya tidak 100% yakin) adalah

async function* getVal() {...}

Fungsi generator ES2016 bekerja seperti ini. Saya telah membuat database handler yang berbasis di atas membosankan yang Anda program seperti ini

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

Perhatikan bagaimana saya memprogramnya seperti sinkron biasa terutama di

yield connection.execSql dan pada yield connection.callProcedure

Fungsi db.exec adalah generator berbasis Promise yang cukup tipikal

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.dll
sumber
4
" async adalah fungsi generator yang disempurnakan " - tidak, sebenarnya tidak.
Bergi
Seperti yang dinyatakan di atas - 'fungsi async' memang mengembalikan Promise. Setidaknya secara konseptual, poin utama dari pernyataan 'async' adalah untuk menggabungkan nilai kembalian fungsi itu dalam sebuah promise. Anda bahkan dapat 'menunggu' pada fungsi lama yang menghasilkan Promise, dan semuanya berfungsi, karena 'async function' === 'function return Promise'.
Spechter
2
@bergi, sebenarnya, ini adalah fungsi generator yang disempurnakan. fungsi generator yang selalu mengembalikan janji .. atau sesuatu.
Alexander Mills