非同期関数が値ではなくPromise {<pending>}を返すのはなぜですか?


127

私のコード:

let AuthUser = data => {
  return google.login(data.username, data.password).then(token => { return token } )
}

そして、私がこのようなものを実行しようとすると:

let userToken = AuthUser(data)
console.log(userToken)

私は得ています:

Promise { <pending> }

しかし、なぜ?

私の主な目標はgoogle.login(data.username, data.password)、promiseを返すトークンを変数に取得することです。その後、いくつかのアクションを実行します。


1
@LoïcFaure-ラクロワ、この記事を参照してください。medium.com/@bluepnume/...は
Srcの

@LoïcFaure-Lacroix getFirstUser関数を見る
Src

それではどうですか?これはpromiseを返す関数です。
ロイック・フォーレ-ラクロワ

1
@LoïcFaure-Lacroixつまり、この例でも、getFirstUser関数で返されるデータプロミスにアクセスするために使用する必要があるということですか?
Srcの

その例では、はい、唯一の他の方法は、解決のように見えるES7構文「await」を使用して、現在のコンテキストの実行を停止し、promiseの結果を待つことです。記事を読むと、それが表示されます。しかし、ES7はおそらくほとんどどこでもまだサポートされていないので、はい。「それから」はほとんどそれです。
ロイック・フォーレ-ラクロワ

回答:


175

結果がまだ解決されていない限り、promiseは常に保留中のログを記録します。.thenpromiseの状態(解決済みまたは保留中)に関係なく結果をキャプチャするには、promiseを呼び出す必要があります。

let AuthUser = function(data) {
  return google.login(data.username, data.password).then(token => { return token } )
}

let userToken = AuthUser(data)
console.log(userToken) // Promise { <pending> }

userToken.then(function(result) {
   console.log(result) // "Some User token"
})

何故ですか?

約束は順方向のみです。解決できるのは1回だけです。aの解決された値は、Promiseその.thenor .catchメソッドに渡されます。

細部

Promises / A +仕様によると:

promiseの解決手順は、promiseと値を入力として受け取る抽象的な操作であり、[[Resolve]](promise、x)と表記します。xがthenableである場合、xは少なくともいくらかpromiseのように動作するとの仮定の下で、promiseにxの状態を採用させようとします。それ以外の場合は、値xでpromiseを満たします。

thenablesのこの処理により、Promises / A +準拠のthenメソッドが公開されている限り、promise実装を相互運用できます。また、Promises / A +実装は、妥当なthenメソッドを使用して、非準拠の実装を「同化」することができます。

この仕様は解析が少し難しいので、分解してみましょう。ルールは:

.thenハンドラーの関数が値を返す場合、Promiseその値で解決されます。ハンドラーが別のハンドラーを返す場合、PromiseオリジナルPromiseはチェーンされた解決済みの値で解決されますPromise。次の.thenハンドラーには常に、前ので返されたチェインされたpromiseの解決された値が含まれます.then

実際に機能する方法を以下で詳しく説明します。

1. .then関数の戻り値は、promiseの解決された値になります。

function initPromise() {
  return new Promise(function(res, rej) {
    res("initResolve");
  })
}

initPromise()
  .then(function(result) {
    console.log(result); // "initResolve"
    return "normalReturn";
  })
  .then(function(result) {
    console.log(result); // "normalReturn"
  });

2. .then関数がを返す場合、Promiseそのチェーンされたpromiseの解決された値が次のに渡されます.then

function initPromise() {
  return new Promise(function(res, rej) {
    res("initResolve");
  })
}

initPromise()
  .then(function(result) {
    console.log(result); // "initResolve"
    return new Promise(function(resolve, reject) {
       setTimeout(function() {
          resolve("secondPromise");
       }, 1000)
    })
  })
  .then(function(result) {
    console.log(result); // "secondPromise"
  });

最初の1つは機能していません。Uncaught SyntaxError: Unexpected token .。2つ目は返却が必要ですPromise
ザミル、

@zamilでは、2番目の例のように関数を呼び出す必要があります。.then呼び出されていない関数は使用できません。回答を更新しました
Bamieh

1
これをブックマークして、いつまでも保存できるようにしています。私はプロミスを実際に構築する方法の本当に明確で読みやすいルールを見つけるために非常に長い間取り組んできました。Promises / A +仕様のブロッククォートは、Promiseを自習するためのPITAである理由の完璧な例です。また、レッスン自体を混乱させなかった場所でsetTimeoutが使用されたのを目にした唯一の時間でもあります。そして、優れたリファレンス、ありがとうございます。
monsto

21

私はこの質問が2年前に尋ねられたことを知っていますが、私は同じ問題に遭遇し、問題の答えはES6からですawait

let AuthUser = function(data) {
  return google.login(data.username, data.password).then(token => { return token } )
}

let userToken = await AuthUser(data)
console.log(userToken) // your data

3
は必要ありません.then(token => return token)。これは不必要なパススルーです。単にgoogleログインコールを返します。
ソビエト連邦

この回答は質問とは無関係です。元のポスターの問題は、ES6の非同期/待機とは関係ありません。この新しい構文糖がECMAScript 2017に導入される前にプロミスが存在し、プロミスを「内部」で使用していました。MDN on async / awaitを参照してください。
try-catch-finally

ES8 / Nodejsの場合await、非同期関数の外部で使用するとエラーがスローされます。おそらく、ここでのより良い例は、AuthUser関数を作成することです。この関数asyncは、次のように終了しますreturn await google.login(...);
Jon L.

4

thenこの方法は、呼び出しで登録された結果ハンドラの戻り値によって非同期的に解決することができ、保留中の約束を返すthenと呼ばれるハンドラ内でエラーを投げることによって、または拒否を。

したがって、呼び出しAuthUserは突然ユーザーを同期的にログインさせませんが、ログインが成功(または失敗)した後に登録されたハンドラーが呼び出されるpromiseを返します。thenログインプロミスの条項によってすべてのログイン処理をトリガーすることをお勧めします。EGは名前付き関数を使用してフローのシーケンスを強調表示します。

let AuthUser = data => {   // just the login promise
  return google.login(data.username, data.password);
};

AuthUser(data).then( processLogin).catch(loginFail);

function processLogin( token) {
      // do logged in stuff:
      // enable, initiate, or do things after login
}
function loginFail( err) {
      console.log("login failed: " + err);
}

1

PromiseのMDNセクションを参照してください特に、then()の戻り値の型を見てください

ログインするには、ユーザーエージェントはサーバーにリクエストを送信し、レスポンスを受信するまで待機する必要があります。リクエストの往復中にアプリケーションを完全に停止させると、通常、ユーザーエクスペリエンスが低下するため、実際にログインする(または他の形式のサーバー操作を実行する)すべてのJS関数は、Promiseなどを使用します、非同期で結果を提供します。

ここで、returnステートメントは常に、それらが出現する関数のコンテキストで評価されることにも注意してください。

let AuthUser = data => {
  return google
    .login(data.username, data.password)
    .then( token => {
      return token;
    });
};

このステートメントreturn token;は、渡される匿名関数がthen()トークンを返す必要があることを意味しており、関数が返す必要があることを意味していませんAuthUser。何AuthUserの呼び出しの結果を返すでgoogle.login(username, password).then(callback);の約束であることを起こるそれは、。

最終的にあなたのコールバック token => { return token; }は何もしません。代わりに、への入力then()は、実際にトークンを何らかの方法で処理する関数である必要があります。


@Src質問者が値を同期的に返す方法を探していて、コードスニペットで推測できるものを超えて開発環境や言語バージョンを想定せずに答えを明らかにする前に、私は回答を書きました-つまり、安全ですES6を前提としていますが、必ずしもES7とは限りません。
Jesse Amano

@AhmadBamieh大丈夫、やります。問題はreturn、new(ish)クロージャー構文でどのように処理されるかを誤解していることだと思います。
Jesse Amano

2
@AhmadBamiehええ、私は実際にその部分を知っていました。それが逆効果だと主張するのとは対照的にtoken => { return token; } 何もしないと断言したのそのためです。google.login(username, password).then(token=>{return token;}).then(token=>{return token;})永遠に言うことなどはできますがPromise、トークンで解決するを返すだけgoogle.login(username, password);です。それをそのままにした場合と同じです。これが「非常に間違っている」と思う理由がわかりません。
Jesse Amano

1
@AhmadBamieh:このテキストの何が問題になっているのか、より具体的に説明できますか?私は何も見てreturn tokenいませんが、OPがおそらく期待どおりに機能しない理由を説明しています。
Bergi

3
@AhmadBamieh:確かに誤解があります。私たち3人は、promiseがどのように機能するかをよく知っています。ステートメントはpromise.then(result => { return result; })とまったく同じですpromise。したがって、メソッド呼び出しは何もせず、コードを簡略化して読みやすくするために削除する必要があります。これは完全に真のステートメントです。
Bergi

0

あなたの約束は保留中です。

userToken.then(function(result){
console.log(result)
})

残りのコードの後。このコードが行うことは.then()、約束を完了し、最終結果を結果変数に取り込み、結果をコンソールに出力することです。結果をグローバル変数に格納することはできません。その説明があなたに役立つことを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.