试图了解Promise()

时间:2017-07-15 20:19:50

标签: javascript node.js promise

所以我已经分叉了一个Javascript项目并尝试扩展它,同时从中学习。我的Javascript技能真的很新鲜,但我认为这很有趣。然而,我正在努力应对所有的承诺,以至于我有许多承诺和风格,我真的不明白它是如何定位的。我没有得到最终结果而且无法理解为什么。

所以我从头开始(我禁用了一个功能以保持简单):

export const calculateMovingAverage = (event, context, callback) =>
  Promise.all([
    // ma.calculateMovingAverage('Kraken', 'AskPrice'),
    ma.calculateMovingAverage('Coinbase', 'SellPrice'),
  ])
    .then((tx) => {
      console.log('tx', tx);
    }).catch((err) => {
      console.error('err', err);
      callback({ statusCode: 500, body: { message: 'Something went wrong' } });
    });

因此调用ma.calculateMovingAverage()

calculateMovingAverage(exchange, metric) {
    const self = this;
    const args = {
      minutes: 10,
      period: 60,
      currency: `${self.cryptoCurrency}`,
      metricname: `${metric}`,
      localCurrency: `${self.localCurrency}`,
      namespace: `${exchange}`,
    };
    var promisedland = new Promise((resolve, reject) => {
      self.cloudwatch.getMetricStatistics(args, (err, data) => {
        if (err) { return reject(err); }

        if (!Array.isArray(data.Datapoints) || !data.Datapoints.length) { return reject("No Datapoints received from CloudWatch!")}

        data.Datapoints.forEach(function(item) {
          self.ma.push(item.Timestamp, item.Average);
        });

        resolve(ma.movingAverage());
      })
    })
    promisedland.then((results) => {
      return new Promise((resolve, reject) => {
        const body = {
          value: results,
          metricName: `${metric} @ 180 MovingAverage`,
          namespace: `${exchange}`
        };
        self.cloudwatch.putAverageMetricData(body, function(err, result) {
          if (err) { return reject(err); }
          resolve(result);
        });
      }
    )
    }).catch(function(err) {
      return reject(err);
    });
  }

现在您可以在calculateMovingAverage()中看到,我尝试调用两个AWS方法。 getMetricStatisticsputAverageMetricData

第一个,getMetricStatistics函数可以很好地工作,因为我很好地从AWS中获取了Datapoints。

功能本身:

  getMetricStatistics(args) {
    return this.cloudwatch.getMetricStatistics({
      EndTime: moment().subtract(180, 'minutes').utc().format(),
      Dimensions: [
        {
          Name: 'CryptoCurrency',
          Value: args.currency
        },
        {
          Name: 'LocalCurrency',
          Value: args.localCurrency
        },
        {
          Name: 'Stage',
          Value: process.env.STAGE || 'dev'
        }
      ],
      MetricName: args.metricname,
      Period: Number(args.period),
      StartTime: moment().subtract(190, 'minutes').utc().format(),
      Statistics: ['Average'],
      Unit: 'Count',
      Namespace: args.namespace || 'Coinboss',
    }).promise();
  }

接下来我想通过MovingAverage模块传递响应,并希望通过putAverageMetricData函数将MA的结果放入CloudWatch指标:

putAverageMetricData(args) {
    return this.cloudwatch.putMetricData({
      MetricData: [
        {
          MetricName: args.metricName,
          Timestamp: moment().utc().format(),
          Unit: 'Count',
          Value: Number(args.value),
        },
      ],
      Namespace: args.namespace || 'Coinboss',
    }).promise()
    .then(function(result) {
      console.log('putAverageMetricData', result);
    });
  }

这是我迷路的地方。我看起来数据永远不会到达putAverageMetricData函数。控制台输出仅向我显示console.log('tx', tx);以及以下内容:

  

2017-07-15T19:37:43.670Z 118ff4f0-6995-11e7-8ae7-dd68094efbd6 tx [   未定义]

好的,所以我没有返回calculateMovingAverage()then。它解决了未定义的错误。我仍然没有从putAverageMetricData()函数中获取日志,这让我觉得我还在遗漏一些东西。

我希望有人能指出我正确的方向

3 个答案:

答案 0 :(得分:1)

您的getMetricStatisticsputAverageMetricData方法已经返回承诺,因此请避开calculateMovingAverage中的Promise constructor antipattern!并且最后不要忘记return承诺:

calculateMovingAverage(exchange, metric) {
  const args = {
    minutes: 10,
    period: 60,
    currency: this.cryptoCurrency,
    metricname: metric,
    localCurrency: this.localCurrency,
    namespace: exchange,
  };
  return this.cloudwatch.getMetricStatistics(args).then(data => {
    if (!Array.isArray(data.Datapoints) || !data.Datapoints.length)
      throw new "No Datapoints received from CloudWatch!";

    for (const item of data.Datapoints) {
      this.ma.push(item.Timestamp, item.Average);
    }
    return this.ma.movingAverage();
  }).then(results => {
    const body = {
      value: results,
      metricName: `${metric} @ 180 MovingAverage`,
      namespace: exchange
    };
    return this.cloudwatch.putAverageMetricData(body);
  });
}

答案 1 :(得分:0)

calculateMovingAverage中,您必须返回您正在创建的承诺,否则Promise.all无法告知承诺何时解决。

return promisedland.then((results) => {
  ...
}).catch(...);

答案 2 :(得分:0)

我没有直接回答如何修复此代码,而是会给你Promises 101课程,我认为你将能够理解更高层次的内容。

回调函数

JavaScript(通常)是“单线程”,意味着一次只能执行一行代码。有时,我们需要做的事情很长,比如向我们的服务器发出请求。为了解决这个问题,javascript使用了回调函数。回调函数是将函数作为参数传递给另一个函数时。最基本的例子是settimout函数。

setTimeout(function() {
  // settimout takes a callback function
}, 1000);

现在,回调的神奇之处在于它将不会被执行,直到所有其他“非回调”代码或“同步”代码

setTimeout(function(error, goodStuff) {
  console.log("WHAAT WHY AM I SECOND?") //this gets printed second
}, 1000);

console.log("YAY! I GET TO GO FIRST!!!!") // this is printed first

这称为javascript事件循环。这是我做的一个小图片,让您了解它是什么:

Javascript事件循环

enter image description here

正如您所看到的那样,调用堆栈异步队列。所有不是回调或异步的代码都将转到调用堆栈。同步特朗普函数将切断线并首先运行。回调函数将转到消息队列或事件循环,并等待所有同步函数完成。同步函数的调用堆栈完成后,回调将开始执行。但最终,javascript程序员遇到了一个小问题。

回调地狱和承诺

Javascript开始变得越来越复杂,最终,回调开始进行回调。他们越是嵌套,他们就越难读。看看这个眼睛疼痛:

fs.readdir(source, function (err, files) {
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function (filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function (err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function (width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
              if (err) console.log('Error writing file: ' + err)
            })
          }.bind(this))
        }
      })
    })
  }
})

所以为了让这样的东西更容易阅读,承诺诞生了!代码就像上面的回调一样,让它逐行阅读。

myfirstPromise().then((resultFromFirstPromise) => {
    return mySecondPromise();
  }).then((resultFromSecondPromise) => {
    return myThirdPromise();
  }).then((resultFromThirdPromise) => {
    //do whatever with the result of the third promise
  }).catch((someError) => {
    //if any of the promises throw an error it will go here!
  })

因此,将这些概念应用于您的代码,这就是我们想要做的事情:

  getMetricStatistics(options)
    .then(metricStatistics => {
      // do what you need to do with those stats

    return putAverageMetricData(metricStatistics);
    })
    .then((putMetricDataResult => {
      //do what you need to do with the metric data result
    }))
    .catch(err => {
      //do error stuff
    })