【问题标题】:Promise resolved before entire chain of promises is resolved承诺在整个承诺链解决之前解决
【发布时间】:2019-11-25 13:51:18
【问题描述】:

我在我的应用中有一系列承诺作为函数。我的每个服务函数都返回一个deferred.promise;

考虑到以下场景,主要 getUser 服务异步调用 getUserPreferences 和 getUserFavourites,解决 getUserData 后的 console.log 甚至在 getUserFavourites 响应之前就被解析! getUserFavourites 响应后,不应该解决 getUserData 中的承诺吗?

事实上,在调用 getUserFavourites 之前,console.log 中的“获取所有用户数据”就在控制台中。从字面上看,在 getUser 几乎像getUserData().then( 一样响应之后,只解决了顶级承诺并使底层 2 异步...

我在这里做错了什么?

var user = 'blabla';

function getUserData() {

   var deferred = $q.defer();

   getUser(user).then(
       function(response) {
          getUserPreferences(response.user).then(
             function(preferences) {
                console.log('preferences', preferences); 
             },
             function() {
                deferred.reject();
             }
          );

          getUserFavourites(response.user).then(
             function(favourites) {
                deferred.resolve();
                console.log('favourites', favourites);
             },
             function() {
                deferred.reject();
             }
          );
       },
       function() {
          deferred.reject();
       }
    );

    return deferred.promise;
}
getUserData().then(
   function() {
      console.log('got all user data');
   }
);

【问题讨论】:

  • 听起来不太可能......是您的 console.log 在您的 deferred.resolve();问题?如果你交换它们,它会达到你的预期吗?
  • @MathewBerg,不,这不是问题。

标签: javascript angularjs promise


【解决方案1】:

一种解决方法是使用async/await 使代码看起来同步。

var user = 'blabla';

async function getUserData() {

  try {
    var deferred = $q.defer();

    let userInfo = await getUser(user)
    let userPrefs = await getUserPreferences(userInfo.user)
    console.log('preferences', userPrefs);

    let userFavourites = await getUserFavourites(userInfo.user)
    deferred.resolve();
    console.log('favourites', userFavourites);
    return deferred.promise;
  } catch (error) {
    deferred.reject();
  }
}

getUserData().then(
   function() {
      console.log('got all user data');
   }
);

【讨论】:

  • 此解决方案的 2 个问题:1. 他使用 angular,因此使用异步会变得很麻烦,2. 此代码未并行执行 getUserPreferences 和 getUserFavorites。
  • async/await 返回的 ES6 承诺未与 AngularJS 框架集成。 AngularJS 通过提供自己的事件处理循环来修改正常的 JavaScript 流程。这将 JavaScript 拆分为经典和 AngularJS 执行上下文。只有在 AngularJS 执行上下文中应用的操作才能受益于 AngularJS 数据绑定、异常处理、属性监视等。
【解决方案2】:

使用$q.all返回一个复合promise:

function getUserData() {    
    return getUser(user).then(function(response) {
        var preferencesPromise = getUserPreferences(response.user);
        var favouritesPromise =  getUserFavourites(response.user);
        return $q.all([preferencesPromise, favouritesPromise]);
    });
}

然后从复合promise中提取数据:

getUserData().then([preferences, favourites] => {
    console.log('got all user data');
    console.log(preferences, favourites);
}).catch(function(error) {
    console.log(error);
});

$q.all 方法返回一个单一的 Promise,该 Promise 将通过值的数组/哈希值解析,每个值对应于 Promise 数组/哈希中相同索引/键处的 Promise。如果任何一个 Promise 以一个拒绝的方式解决,这个产生的 Promise 将以相同的拒绝值被拒绝。

有关详细信息,请参阅

【讨论】:

  • 在我的情况下,getUserData 收藏夹是未定义的,因为它需要的时间比首选项要长。不知道为什么,但一旦 getUser 得到解决,它就不会等待以下两个承诺......
【解决方案3】:

您必须返回嵌套的 Promise 才能拥有链。 这里的问题是你有 2 个嵌套的 Promise,所以你需要返回一个 Promise.all (或 $q.all 在你的情况下)获取getUserPreferencesgetUserFavorites 返回的 2 个 Promise 的数组:

var user = 'blabla';

function getUserPreferences(){
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      return resolve({color: 'green'});
    },500);
  });
}

function getUserFavorites(){
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      return resolve([{id: 1, title: 'first favorite'}, {id: 2, title: 'second favorite'}]);
    },500);
  });
}

function getUser(){
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      return resolve(user);
    },500);
  });
}

function getUserData() {
   
   return getUser().then(
       function(user) {
          console.log(user);
          var prefPromise = getUserPreferences(user).then(
             function(preferences) {
                console.log('preferences', preferences); 
                return preferences;
             },
             function(error) {
                console.log("Error getting preferences");
                throw error;
             }
          );

          var favPromise = getUserFavorites(user).then(
             function(favourites) {
                console.log('favourites', favourites);
                return favourites;
             },
             function(error) {
                console.log("Error getting favorites");
                throw error;
             }
          );
          
          return Promise.all([
            prefPromise,
            favPromise
          ]);
       },
       function(err) {
          console.log("Error getting user");
          throw err;
       }
    );
}

getUserData().then(
   function(results) {
      console.log(results);
   }
);

请注意,出于演示目的,我使用的是 es6 Promise 而不是 angular $q,但精神是一样的:

$q.defer() => new Promise()
$q.all     => Promise.all

由于 Promise 模式非常适合简化异步代码并使其看起来像同步代码,因此您可以将上面的示例简化为:

var user = { name: 'blabla'};

function getUserPreferences(user){
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      return resolve({color: 'green'});
    },500);
  });
}

function getUserFavorites(user){
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      return resolve([{id: 1, title: 'first favorite'}, {id: 2, title: 'second favorite'}]);
    },500);
  });
}

function getUser(){
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      return resolve(user);
    },500);
  });
}

function getUserData() {
   return getUser()
   .then(user => { // user is resolved
      // running parallel promises to get user infos:
      return Promise.all([
        user,
        getUserPreferences(user),
        getUserFavorites(user)
      ]);
    })
    .then(results => {
      // wrapping the results into something more semantic:
      let userData = results[0]; 
      userData.prefs = results[1];
      userData.favs = results[2];
      return userData;
    });
}

getUserData().then(
   function(userData) {
      console.log('Final result:');
      console.log(userData);
   }
);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-24
    • 2021-08-11
    • 2015-06-23
    • 2019-04-13
    • 1970-01-01
    • 2016-12-06
    • 1970-01-01
    相关资源
    最近更新 更多