簡體   English   中英

試圖了解Promise()

[英]Trying to understand 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

第一個,當我從AWS很好地獲取數據點時,getMetricStatistics函數可以很好地工作。

函數本身:

  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 Metrics:

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 [未定義]

好的,所以我沒有返回then的calculateMovingAverage()。 它解決了未定義的錯誤。 我仍然沒有從putAverageMetricData()函數獲取日志,這讓我覺得我仍然缺少一些東西。

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

您的getMetricStatisticsputAverageMetricData方法已經返回了Promise ,因此請避免在calculateMovingAverage使用Promise構造函數反模式 並且不要忘了從那里最終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);
  });
}

calculateMovingAverage您必須返回正在創建的承諾,否則Promise.all無法確定承諾何時得到解決。

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

除了向您提供有關如何修復此代碼的直接答案之外,我還將向您提供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事件循環

在此處輸入圖片說明

如您所見,這里有調用堆棧異步隊列 您所有非回調或異步代碼都將轉到調用堆棧。 同步王牌功能將切斷線路並首先運行。 回調函數將轉到消息隊列或事件循環,並等待所有同步函數完成。 同步函數的調用堆棧完成后,回調將開始執行。 但是最終,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
    })

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM