web-dev-qa-db-ja.com

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

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

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

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

しかし、それは一貫性がありません... doSomethingAsync()がpromiseを返すと仮定すると、awaitキーワードはpromiseの値ではなくpromiseから値を返し、getVal関数shouldはそれを返します暗黙の約束ではなく価値。

それでは、正確には何ですか? asyncキーワードでマークされた関数は、暗黙的にプロミスを返しますか、それとも返すものを制御しますか?

おそらく、明示的に何かを返さなければ、暗黙的に約束を返すのでしょうか...?

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

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は無視されます。

79
Alexander Mills

戻り値は常に約束です。 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);
93
Nathan Wall

私は仕様を見て、次の情報を見つけました。短いバージョンでは、async functionPromisesを生成するジェネレーターに脱糖されます。したがって、はい、非同期関数はpromisesを返します。

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

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); });
    });
}
17
Jon Surrell