Правильный способ запуска асинхронной функции в Promise - PullRequest
0 голосов
/ 22 апреля 2019

Я создаю тестовое приложение, используя systeminformation .Я пытаюсь сделать так, чтобы каждый then ждал завершения предыдущей функции.Проблема, с которой я сталкиваюсь, состоит в том, что функции, которые я выполняю внутри, также обещают, поэтому следующие then запускаются до завершения функции.

const si = require('systeminformation');

var cpuObj;

function initCPU() {

  return new Promise(resolve => {

    si.cpu()
      .then(data => cpuObj = data)
      .catch(err => console.log(err))
    .then(() => {
      setTimeout(() => console.log("timer"), 3000);
    })
    .then(() => {
      si.cpuTemperature().then(data => console.log(data));
    })
    .then(() => {
      console.log("here");
    });
  });
}

function test() {
  console.log(cpuObj);
}

initCPU().then(() => {
  test();
});

Вывод:

here
{ main: -1, cores: [], max: -1 }
timer

Ожидаемый результат:

{ main: -1, cores: [], max: -1 }
timer
here

1 Ответ

1 голос
/ 22 апреля 2019

Несколько моментов, на которые необходимо обратить внимание:

  • setTimeout() не возвращает обещание, поэтому вам нужно дать обещание и вернуть его .
  • Выровняйте цепочку, возвращая обещания из каждого продолжения, а не пытайтесь связать продолжения внутри других продолжений (т. Е. then() внутри then()).
  • Не заключайте цепочку продолжения в конструктор обещаний, поскольку сама цепочка уже является обещанием, просто вместо этого верните ее напрямую. Это считается антипаттерном .
  • Не используйте глобальные переменные, потому что это делает initCPU() больше не входящим безопасным. Многократные вызовы на initCPU() до разрешения обещания, возвращенного первым вызовом, в противном случае приведут к неожиданному поведению. Вместо этого используйте соответствующую область для передачи значений, которые в данном случае являются самой функцией.
  • Разрешить ошибкам распространяться на вызывающего и разрешать вызывающему решать, как обработать ошибку. Не обрабатывайте ошибки из initCPU(), если только вы не планируете использовать запасной вариант и продолжаете предоставлять вызывающей стороне значимые данные.
const si = require('systeminformation');
const delay = ms => new Promise(resolve => { setTimeout(resolve, ms); });

function initCPU() {
  // use local scope, not global
  let cpuObj;

  // return this promise chain directly
  return si.cpu()
    .then(data => {
      cpuObj = data;
      // return the promise to the chain
      return delay(3000);
    })
    // let caller handle errors
    // .catch(err => console.log(err))
    // flatten your chain
    .then(() => {
      console.log('timer');
      // return the promise to the chain
      return si.cpuTemperature();
    })
    // flatten your chain
    .then(data => {
      console.log(data);
      console.log('here');
      // pass data to caller
      return cpuObj;
    });
}

function test(cpuObj) {
  // received from last continuation of initCPU()
  console.log(cpuObj);
}

initCPU()
  .then(test)
  // handle error from caller
  .catch(err => {
    console.log(err);
  });

Если вы просто хотите немедленно запросить объект cpu и запросить cpuTemperature через 3 секунды, я бы сделал что-то подобное, используя Promise.all():

// default to 3 seconds, allow it to be configurable
function initCPU(ms = 3000) {
  return Promise.all([
    si.cpu(),
    delay(ms).then(() => si.cpuTemperature())
  ]).then(([cpu, cpuTemperature]) => ({
    cpu,
    cpuTemperature
  }));
}

function test (obj) {
  console.log(obj.cpu);
  console.log(obj.cpuTemperature);
}

initCPU()
  .then(test)
  .catch(err => {
    console.log(err);
  });
...