Чому моя асинхронна функція повертає Promise {<pending>} замість значення?


129

Мій код:

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)якого повертає обіцянку, в змінну. І лише тоді заздалегідь заздалегідь виконайте деякі дії.


1
@ LoïcFaure-Lacroix, дивіться цю статтю: medium.com/@bluepnume/…
Src

@ LoïcFaure-Lacroix дивись на getFirstUserфункцію
Src

То що з цим? Це функція, що повертає обіцянку.
Loïc Faure-Lacroix

1
@ LoïcFaure-Lacroix, значить, навіть у цьому прикладі нам потрібно скористатися, щоб отримати доступ до даних, які обіцяють повернутися у функцію getFirstUser?
Src

У цьому прикладі так, єдиний інший спосіб - використовувати синтаксис ES7 "очікувати", який, здається, вирішує зупинити виконання поточного контексту, щоб очікувати на результат обіцянки. Якщо ви прочитаєте статтю, ви побачите її. Але оскільки ES7, мабуть, майже ніде ще не підтримується, так. "Тоді" це майже все.
Loïc Faure-Lacroix

Відповіді:


176

Обіцянка завжди буде зафіксованою до тих пір, поки її результати ще не вирішені. Потрібно закликати .thenобіцяти зафіксувати результати незалежно від стану обіцянки (вирішено чи ще не очікується):

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

Чому так?

Обіцянки мають лише напрямок вперед; Вирішити їх можна лише один раз. Розв’язане значення a Promiseпередається його .thenабо .catchметодам.

Деталі

Відповідно до специфікацій Обіцянки / A +:

Процедура розв’язування обіцянки - це абстрактна операція, яка приймає як вхід обіцянку та значення, які ми позначаємо як [[Розв’язати]] (Обіцянка, х). Якщо x є підданим, він намагається змусити обіцянку прийняти стан x, при припущенні, що x поводиться принаймні дещо як обіцянка. В іншому випадку він виконує обіцянку зі значенням x.

Таке поводження з thenables дозволяє взаємодії з обіцянками взаємодіяти, якщо вони виявляють метод Promis / A +-compliant, а потім. Це також дозволяє реалізаціям Promises / A + "засвоювати" невідповідні реалізації з розумними методами.

Цю специфікацію трохи важко розібрати, тому давайте її розбимо. Правило таке:

Якщо функція .thenобробника повертає значення, то Promiseрозв'язується з цим значенням. Якщо обробник повертає інший Promise, то оригінал Promiseвирішується з розділеним значенням ланцюжка Promise. Наступний .thenобробник завжди буде містити вирішене значення ланцюгової обіцянки, повернене в попередньому .then.

Як саме це працює, описано нижче більш докладно:

1. Повернення .thenфункції буде вирішеним значенням обіцянки.

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функція повертає a 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"
  });

Ваш перший не працює. Uncaught SyntaxError: Unexpected token .. Другий потребує поверненняPromise
заміль

@zamil, ви повинні викликати функцію, як у другому прикладі. Ви не .thenможете надіслати функцію, що не викликається. оновив відповідь
Баміє

1
Я робила закладки на цьому, щоб я міг зберегти його назавжди. Я дуже довго працюю над тим, щоб знайти справді чіткі та зрозумілі правила, як насправді будувати обіцянки. Ваша блокова котировка Promises / A + spec - це прекрасний приклад того, чому було ПДТА, щоб самостійно навчати обіцянки. Це також ТІЛЬКИ час, коли я бачив setTimeout, використовуваний там, де він не переплутав сам урок. І відмінна довідка, дякую.
Монсто

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 про асинхронізацію / очікування .
try-catch-нарешті

Для ES8 / Nodejs, помилки викидаються при використанні awaitпоза функцією асинхронної. Можливо, кращим прикладом тут було б зробити AuthUserфункцію async, яка потім закінчуєтьсяreturn await google.login(...);
Джон Л.

4

thenМетод повертає очікує обіцянку , яке може бути вирішено асинхронно повертається значенням результуючого обробника , зареєстрованим у виклику then, або відкинутий метання помилки всередині обробника викликається.

Таким чином, виклик AuthUserне буде раптово входити в систему синхронно, але повертає обіцянку, чиї зареєстровані тоді обробники будуть викликані після входу (або помилки). Я б запропонував запустити всю обробку входу за допомогою 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

Дивіться розділ MDN про Обіцянки. Зокрема, подивіться на тип повернення тоді ().

Для входу в систему користувач-агент повинен подати запит на сервер і чекати отримання відповіді. Оскільки ваша програма повністю зупиняє виконання під час зворотного запиту, як правило, спричиняє поганий досвід роботи користувача, практично кожна функція JS, яка входить у вас (або виконує будь-яку іншу форму взаємодії з сервером), буде використовувати Обіцянку або щось дуже подібне , щоб асинхронно доставити результати.

Тепер також зауважте, що 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.
Джессі Амано

@AhmadBamieh Добре, зробимо. Я припускаю, що проблема полягає в тому, що я неправильно зрозумів, як returnставиться до нового (іш) синтаксису закриття, в такому випадку - ну, я категорично не схвалюю це, але помилка все-таки моя, і я вибачаюся за це.
Джессі Амано

2
@AhmadBamieh Ер, я насправді знав цю частину, і тому я запевнив, що token => { return token; } нічого не робить , на відміну від того, щоб стверджувати, що це було непродуктивно. Ви можете говорити google.login(username, password).then(token=>{return token;}).then(token=>{return token;})і так далі назавжди, але ви повернетесь лише тим, Promiseщо вирішується за допомогою маркера - так само, як якщо б ви просто залишили його як google.login(username, password);. Я не впевнений, чому ви вважаєте, що це "дуже неправильно".
Джессі Амано

1
@AhmadBamieh: чи можете ви бути більш конкретними щодо того, що в цьому фрагменті тексту не так? Я нічого не бачу, він просто пояснює, чому return tokenне працює так, як, напевно, очікував ОП.
Бергі

3
@AhmadBamieh: насправді є непорозуміння. Ми всі троє добре знаємо, як обіцянки працюють, твердження таке, що promise.then(result => { return result; })точно рівнозначне promise, тому виклик методу нічого не робить і його слід кинути, щоб спростити код і підвищити читабельність - твердження, що цілком відповідає дійсності.
Бергі

1

Ваша Обіця очікує, завершіть її

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

після вашого останнього коду. Все, що робиться цим кодом, - це те, що .then()виконує ваші обіцянки та фіксує кінцевий результат у змінну результатів та результат друку на консолі. Майте на увазі, ви не можете зберігати результат у глобальній змінній. Сподіваємось, що пояснення може вам допомогти.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.