Node.js - обратные вызовы Promise переопределяют пользовательские объекты - PullRequest
0 голосов
/ 05 июля 2019

Это мой первый пост на этом сайте, поэтому я заранее извиняюсь за отсутствие данных или тегов и т. Д. Я использую этот сайт в течение многих лет, и он всегда мне помогал, но теперь я действительно потеряни я нигде не смог найти ответ.

У меня есть приложение, в котором мне нужно вызывать веб-службу 10 раз, каждый раз с другим параметром.Возвращаемая полезная нагрузка сложна, поэтому я создал собственный объект для хранения данных.Мне нужны данные из всех 10 вызовов, прежде чем я перейду к своему коду, что привело меня в ад обратного вызоваЯ пытаюсь использовать Обещания, чтобы упростить это, но именно здесь я сталкиваюсь с этой странной проблемой.Я могу повторить эту проблему с помощью простого класса:

Пользовательский объект (Person):

var person = {
  firstName : String,
  lastName : String,
  age : Number
}

function Person() { //getters and setters
} module.exports = Person;

Функция getToken (возвращает определенный токен, необходимый для вызовов веб-службы), здесь замененс простой строкой:

function getToken() {

  return new Promise(function(resolve, reject) {
      var x = "random token";
      console.log('getting token');
      setTimeout(function(){resolve(x)}, 200);
    });
}

Функция getAction: в моем реальном приложении она вызывает веб-сервис.Здесь он просто создает случайного человека с идентификатором на входе:

function getAction(uuid) {
  return new Promise(resolve => {
      var newPerson = new Person();
      newPerson.setFirstName("John " + uuid);
      newPerson.setLastName("Doe");
      newPerson.setAge(20);
      console.log("---> Returning Person " + newPerson.getFirstName());
      setTimeout(function(){resolve(newPerson)}, 300);
  });
}

Функция getActions: вызывает getAction для каждого входного параметра.Эта функция сама должна возвращать Обещание, потому что есть другая функция, ожидающая, пока все данные не будут доступны, прежде чем продолжить.

function getActions() {

 return new Promise(function(resolve, reject) {

  getToken().then(async function(tokenret) {

      var userIds =   ["001", "002", "003", "004", "005", "006", "007", "008", "009", "010" ];

      var myPromise = Promise.join;
      myPromise(getAction(userIds[0]), getAction(userIds[1]), getAction(userIds[2]), function(personOne, personTwo, personThree) {
        console.log("Person One: " + personOne.getFirstName());
        console.log("Person Two: " + personTwo.getFirstName());
        console.log("Person Three: " + personThree.getFirstName());
      });

  }).catch(function(rej) {console.log("Promise Failed! " + rej);});

  });
}

Вывод для этого выполнения:

---> Returning Person John 001
---> Returning Person John 002
---> Returning Person John 003
Person One: John 003
Person Two: John 003
Person Three: John 003

Мы можем видетьчто функция getAction была выполнена в правильном порядке с правильными параметрами.Но все 3 переменные, созданные в функции getActions, имеют значение из последнего выполнения.

Я также попробовал этот код:

      const allPromises = userIds.map(userIds => getAction(userIds));

      await Promise.all(allPromises).then(function(allResults) {

        console.log("Received " + allResults.length + " records");
        var thisPersonZero = allResults[0];
        console.log("This person 0: " + thisPersonZero.getFirstName());
        var thisPersonOne = allResults[1];
        console.log("This person 1 " + + thisPersonOne.getFirstName());
        var thisPersonTwo = allResults[2];
        console.log("This person 2 " + + thisPersonTwo.getFirstName());
        console.log("Recapping");
        console.log("This person 0: " + thisPersonZero.getFirstName());
        console.log("This person 1: " + thisPersonOne.getFirstName());
        console.log("This person 2: " + thisPersonTwo.getFirstName());

      });


И я получил такой вывод:

---> Returning Person John 001
---> Returning Person John 002
---> Returning Person John 003
---> Returning Person John 004
---> Returning Person John 005
---> Returning Person John 006
---> Returning Person John 007
---> Returning Person John 008
---> Returning Person John 009
---> Returning Person John 010
Received 10 records
This person 0: John 010
This person 1 John 010
This person 2 John 010
Recapping
This person 0: John 010
This person 1: John 010
This person 2: John 010

Наконец, я попытался использовать await, что привело к еще более странным результатам:

      var firstPerson = await getAction(userIds[0]);
      console.log("First Person: " + firstPerson.getFirstName());
      var secondPerson = await getAction(userIds[1]);
      console.log("Second Person: " + secondPerson.getFirstName());
      console.log("Recapping");
      console.log("First Person: " + firstPerson.getFirstName());
      console.log("Second Person: " + secondPerson.getFirstName());


Результат:

---> Returning Person John 001
First Person: John 001
---> Returning Person John 002
Second Person: John 002
Recapping
First Person: John 002
Second Person: John 002

Таким образом, значение верное до обратного вызова для следующего Promise,который заменяет значение для всех переменных.Поведение остается тем же, если я создаю копии переменной, даже используя JSON.parse (JSON.stringify ()).

Этот код отлично работает, если я использую строки вместо объекта Person.Однако было бы крайне обременительно пытаться сделать это без пользовательских объектов.

Я уверен, что делаю какую-то очень простую ошибку, но даже если это кажется очень простым, я ничего не смог найтипо этому конкретному вопросу в любом месте.Эта проблема возникает в Node версий 9.5 и 10, работающих на MacOS (если это имеет какое-либо значение).

Любая помощь будет очень признательна.Заранее спасибо!

Полный фрагмент кода:

// Person.js


var person = {
  firstName : String,
  lastName : String,
  age : Number
}

function Person() {
  Person.prototype.setFirstName = function(firstName) { person.firstName = firstName; }
  Person.prototype.setLastName = function(lastName) { person.lastName = lastName; }
  Person.prototype.setAge = function(age) { person.age = age; }

  Person.prototype.getFirstName = function() { return (typeof person.firstName === 'undefined') ? '' : person.firstName; }
  Person.prototype.getLastName = function() { return (typeof person.lastName === 'undefined') ? '' : person.lastName; }
  Person.prototype.getAge = function() { return (typeof person.age === 'undefined') ? 0 : person.age; }

  }

  module.exports = Person;





// Error.js

var Promise = require('bluebird');
var Person = require("./models/Person");


function getToken() {

  return new Promise(function(resolve, reject) {
      var x = "random token";
      console.log('getting token');
      setTimeout(function(){resolve(x)}, 200);
    });
}


function getActions() {

 return new Promise(function(resolve, reject) {

  getToken().then(async function(tokenret) {

      var userIds =   ["001", "002", "003", "004", "005", "006", "007", "008", "009", "010" ];
/*
      var myPromise = Promise.join;
      myPromise(getAction(userIds[0]), getAction(userIds[1]), getAction(userIds[2]), function(personOne, personTwo, personThree) {
        console.log("Person One: " + personOne.getFirstName());
        console.log("Person Two: " + personTwo.getFirstName());
        console.log("Person Three: " + personThree.getFirstName());
      });

*/
      var firstPerson = await getAction(userIds[0]);
      console.log("First Person: " + firstPerson.getFirstName());
      var secondPerson = await getAction(userIds[1]);
      console.log("Second Person: " + secondPerson.getFirstName());
      console.log("Recapping");
      console.log("First Person: " + firstPerson.getFirstName());
      console.log("Second Person: " + secondPerson.getFirstName());


/*
      const allPromises = userIds.map(userIds => getAction(userIds));

      await Promise.all(allPromises).then(function(allResults) {

        for (var x = 0; x < allResults.length; x++)
        {
          var thisPerson = allResults[x];
          console.log("This Person: " + thisPerson.getFirstName());
        }

        console.log("Received " + allResults.length + " records");
        var thisPersonZero = allResults[0];
        console.log("This person 0: " + thisPersonZero.getFirstName());
        var thisPersonOne = allResults[1];
        console.log("This person 1 " + thisPersonOne.getFirstName());
        var thisPersonTwo = allResults[2];
        console.log("This person 2 " + thisPersonTwo.getFirstName());
        console.log("Recapping");
        console.log("This person 0: " + thisPersonZero.getFirstName());
        console.log("This person 1: " + thisPersonOne.getFirstName());
        console.log("This person 2: " + thisPersonTwo.getFirstName());

      });
*/
  }).catch(function(rej) {console.log("Promise Failed! " + rej);});

  });
}


function getAction(uuid) {
  return new Promise(resolve => {
      var newPerson = new Person();
      newPerson.setFirstName("John " + uuid);
      newPerson.setLastName("Doe");
      newPerson.setAge(20);
      console.log("---> Returning Person " + newPerson.getFirstName());
      setTimeout(function(){resolve(newPerson)}, 300);
  });
}

getActions();

1 Ответ

0 голосов
/ 05 июля 2019

Не вдаваясь слишком много в ваш код, я уже могу сказать вам, что ваши классовые методы Person используют объект person , объявленный в верхней части файла Person.js .Исправьте те, которые используют this и так и должно быть.

Person.prototype.setFirstName = function(firstName) { this.firstName = firstName; }

То, как вы его установили, теперь методы продолжают мутировать один и тот же объект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...