在Promise中运行异步功能的正确方法

时间:2019-04-22 16:13:10

标签: node.js asynchronous

我正在使用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 个答案:

答案 0 :(得分:1)

需要解决的几点:

  • setTimeout()不会返回承诺,因此您需要承诺并返回
  • 通过从每个延续中返回promise而不是尝试在其他延续中(例如,then()内的then()链接延续)来平铺链。
  • 不要用promise构造函数包装延续链,因为该链本身已经是一个promise,而是直接将其返回。这被认为是an antipattern
  • 请勿使用全局变量,因为它会使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对象,并在3秒钟后查询cpuTemperature,则可以使用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);
  });