async / awaitは暗黙的にpromiseを返しますか?


111

asyncキーワードでマークされた非同期関数が暗黙的にpromiseを返すことを読みました。

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

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

しかし、それは首尾一貫していない...仮定doSomethingAsync()がpromise を返し、awaitキーワードがpromise itefではなくpromiseから値を返す場合、私のgetVal関数暗黙のpromiseではなくその値返す必要があります。

それでは、具体的にはどうなのでしょうか。asyncキーワードでマークされた関数は暗黙的にpromiseを返しますか、それともそれらが返すものを制御しますか?

おそらく、明示的に何かを返さない場合、暗黙的に約束が返されます...?

より明確にするために、上記との違いがあります

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

私の概要では、実際の動作は従来のreturnステートメントと矛盾しています。async関数から非プロミス値を明示的に返すと、プロミスに強制的にラップされるようです。大きな問題はありませんが、通常のJSには対応できません。


1
何がconsole.log表示されますか?
Barmar 2016

これは、promise自体ではなく、promise resolve関数によって渡される値です
Alexander Mills

おそらく待ってから、約束の結果が明らかになります。
Hamlet Hakobyan 2016

実際、私は間違っていました、それは約束を記録しています
アレクサンダーミルズ

2
JavaScriptの約束は、c#の非同期待機動作を模倣することです。ただし、これまではc#でそれをサポートするための多くの構造があり、JavaScriptでは何もありませんでした。そのため、多くのユースケースでは非常に似ているように見えるかもしれませんが、それはやや誤称です。
トラビスJ

回答:


135

戻り値は常に約束です。明示的にpromiseを返さない場合、返される値は自動的にpromiseにラップされます。

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

があっても同じことです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));

自動アンラップを約束するため、async関数内から値の約束を返す場合、値の約束を受け取ります(値の約束の約束ではありません)。

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

私の概要では、動作は確かに従来のreturnステートメントと矛盾しています。非同期関数からプロミス以外の値を明示的に返すと、プロミスに強制的にラップされるようです。私は大きな問題はありませんが、通常のJSを無視します。

ES6には、とまったく同じ値を返さない関数がありますreturn。これらの関数はジェネレーターと呼ばれます。

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

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

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

3
静的メソッドPromise.resolveによって「返される値は自動的にpromiseにラップされます」。つまり、非同期関数のreturnステートメントが-return xの場合。それは暗黙的に-Promise.resolve(x);を返します。
adnan2nd 2017年

自分で明示的に作成するのではなく、自動的に作成されたプロミスを返すだけの悪い習慣と見なされていますか?どういうわけか私は多くの場合クリーンなアプローチが好きです。
マーラー

24

スペックを調べたところ、以下の情報が見つかりました。短いバージョンは、s async functionを生成するジェネレータへのデガーPromiseです。したがって、はい、非同期関数はpromiseを返します

tc39仕様によると、次のことが当てはまります。

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

砂糖は:

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

どこでspawn「次のアルゴリズムの呼び出しがあります」:

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

「短いバージョンは、非同期関数がPromiseを生成するジェネレーターにデガーすることです。」と混乱async functionしてasync function*いるかもしれません。前者は単に約束を返します。後者は、promiseを生成するジェネレータを返します。
cdhowie

この回答は、主に仕様への参照であり、確認後、混乱はないと思います。確かに、非同期関数はpromiseを返しますが、これを行うために、promiseを生成するジェネレーターにデシュガーします。
Jon Surrell

-1

呼び出すときに関数の前にawaitを追加するだけです:

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

1
awaitは非同期関数でのみ有効です
Han Van Pham

-3

asyncはpromiseを返しません。awaitキーワードはpromiseの解決を待ちます。asyncは拡張ジェネレーター機能であり、awaitはyieldのように機能します

構文(100%確実ではない)は

async function* getVal() {...}

ES2016のジェネレーター関数は、このように機能します。私はあなたがこのようにプログラムする面倒な上に基づいてデータベースハンドラを作成しました

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

特に、通常の同期のようにプログラムする方法に注意してください。

yield connection.execSql そして yield connection.callProcedure

db.exec関数は、かなり典型的なPromiseベースのジェネレーターです。

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
非同期は拡張ジェネレーター関数です」-いいえ、実際にはそうではありません。
ベルジ

上記のように-「非同期関数」は確かにPromiseを返します。概念的には、「async」ステートメントの主なポイントは、その関数の戻り値をpromiseにラップすることです。「非同期関数」===「PromiseがPromiseを返す」ため、Promiseを返す単純な古い関数を「待つ」こともできます。
2018年

2
@bergi、実際には、それは拡張ジェネレーター機能です。常にpromise ..または何かを返すジェネレーター関数。
アレクサンダーミルズ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.