在递归中使用javascript承诺进行Api调用 [英] Make an Api call with javascript promises in recursion

查看:137
本文介绍了在递归中使用javascript承诺进行Api调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用gitter api从房间获取所有信息。

I would like to use the gitter api to get all the messages from a room.

我需要做的是发送get api请求,例如50项,onComplete我需要发送另外50个项目的请求,并跳过我已经收到的50个项目。做这个请求,直到他们不会返回任何项目。所以:

What I need to do is to send the get api request with e.g. 50 items, onComplete I need to send another request with 50 items and skip 50 items I already received. Do this request till they won't return any items. So:


  • 发送api请求

  • json解析它

  • :请求有项目


    • 使用这个项目进行sql查询

    • 继续查询

    • 发送下一个api请求(递归?)

    • ?如果在下一个api请求中没有更多的项目 - 显示完成消息

    • send api request
    • json parse it
    • : request has items
      • make a sql query with this items
      • proceed the query
      • send next api request (recursion?)
      • ? if no more items in next api request - show done message

      • 中止邮件

      承诺,但我有点困惑与他们,不知道,如果我做的一切正确。如果所有调用都完成,主要问题是下一个Api调用和回调。这是我的代码:

      I am trying Promises for that, but I got a bit confused with them and don't know, if I am doing everything right. The main problem is the next Api call and callback if all calls are done. This is my code:

      class Bot {
        //...
      
        _mysqlAddAllMessages(limit, skip) {
          promise('https://api.gitter.im/v1/rooms/' + this.room + '/chatMessages' +
              '?access_token=' + config.token + '&limit=' + limit + '&skip=' + skip)
              .then(function (response) {
                return new Promise(function (resolve, reject) {
                  response = JSON.parse(response);
      
                  if (response.length) {
                    console.log(`Starting - limit:${limit}, skip:${skip}`);
      
                    resolve(response);
                  }
                })
              }).then(response => {
                let messages = response,
                    query = 'INSERT INTO messages_new (user_id, username, message, sent_at) VALUES ';
      
                for (let message of messages) {
                  let userId = message.fromUser.id,
                      username = message.fromUser.username,
                      text = message.text.replace(/["\\]/g, '|'),
                      date = message.sent;
      
                  query += '("' + userId + '", "' + username + '", "' + text + '", "' + date + '"), ';
                }
      
                query = query.substr(0, query.length - 2);
      
                return new Promise((resolve, reject) => {
                  this.mysql.getConnection((error, connection) => {
                    connection.query(query, (err) => {
                      if (err) {
                        reject(`Mysql Error: ${err}`);
                      } else {
                        connection.release();
      
                        resolve(console.log(`Added ${messages.length} items.`));
                      }
                    });
                  });
                });
              })
              .then(()=> {
                // what to do here
                return this._mysqlAddAllMessagesf(limit, skip += limit)
              })
              .catch(function (er) {
                console.log(er);
              })
              .finally(function () {
                console.log('Message fetching completed.');
              });
        }
      }
      
      let bot = new Bot();
      bot._mysqlAddAllMessages(100, 0);
      

      也许你可以检查并帮助我?或者提供类似的代码这样的东西?

      Maybe you can check and help me? Or provide similar code for such things?

      这是我重写的代码: href =http://jsfiddle.net/swbd4gqb/2/ =nofollow> jsfiddle

      Here is what I refactored the code to: jsfiddle

      推荐答案

      你的代码让我很困惑使用承诺与异步操作最简单的方法是承诺现有的异步操作,然后使用承诺写入所有逻辑。为了promisify,某些东西意味着生成或编写一个返回承诺而不是仅使用回调的包装函数。

      Your code has me pretty confused. The simplest way to use promises with async operations is to "promisify" your existing async operations and then write all the logic after that using promises. To "promisify" something means to generate or write a wrapper function that returns a promise rather than uses only a callback.

      首先,看看整体逻辑。根据您的问题,您表示您有一个API,您希望一次获取50个项目,直到完成所有API。这可以用递归类结构来完成。创建内部函数来检索并返回承诺,并且每次完成后再次调用它。假设你有两个核心函数,一个叫做$ code> getItems(),从你的API获取项目并返回一个承诺,一个叫 storeItems()将这些项目存储在数据库中。

      First, lets look at the overall logic. Per your question, you said you have an API that you want to call to fetch 50 items at a time until you've gotten them all. That can be done with a recursive-like structure. Create an internal function that does the retrieving and returns a promise and each time it completes call it again. Assuming you have two core functions involved here, one called getItems() that gets items from your API and returns a promise and one called storeItems() that stores those items in your database.

      function getAllItems(room, chunkSize, token) {
          var cntr = 0;
          function getMore() {
              return getItems(room, cntr, chunkSize, token).then(function(results) {
                  cntr += results.length;
                  if (results.length === chunkSize) {
                      return storeItems(results).then(getMore);
                  } else {
                      return storeItems(results);
                  }
              });
          }
          return getMore();        
      }
      

      此代码使用了一个稍微高级但非常有用的链接承诺承诺的特点。当您从 .then()处理程序返回承诺时,它将链接到先前的承诺,自动将它们链接到一系列操作中。最后的返回结果或错误然后通过原始承诺返回给原来的呼叫者。类似地,在此链中可能发生的任何错误一直传播回原始调用者。这对于具有多个异步操作的复杂函数非常有用,如果使用常规回调,则不能简单地返回或抛出。

      This code makes use of chaining promises which is a slightly advanced, but extremely useful feature of promises. When you return a promise from a .then() handler, it is chained onto the previous promise, automatically linking them all together into a series of operations. The final return result or error is then returned back through the original promise to the original caller. Similarly, any error that might happen in this chain is propagated all the way back to the original caller. This is extremely useful in complicated functions with multiple async operations where you cannot just simply return or throw if using regular callbacks.

      然后将这样调用:

      getAllItems(this.room, 50, config.token).then(function() {
          // finished successfully here
      }, function(err) {
          // had an error here
      });
      

      现在,我将介绍一些示例,用于创建低级别调用的promisified版本,以实现 getItems() storeItems()。稍等一下。

      Now, I'll work on some examples for created promisified versions of your lower levels calls to implement getItems() and storeItems(). Back in a moment with those.

      我不完全明白你的异步操作的所有细节,所以这不是一个完全正常的例子,但应该说明总体概念,然后您可以询问有关实施的任何必要的澄清问题。

      I don't quite fully understand all the details in your async operations so this will not be a fully working example, but should illustrate the overall concept and you can then ask any necessary clarifying questions about the implementation.

      当承诺功能时,您要做的主要事情是封装肮脏的处理工作回调和错误条件成为一个返回承诺的核心函数。那么这样就可以使用这个很好的干净流程的基于代码的代码,并且允许你使用控制流程中真正伟大的错误处理能力。

      When promisifying a function, the main thing you want to do is to encapsulate the dirty work of handling the callback and error conditions into one core function that returns a promise. That then allows you to use this function in nice clean flowing promise-based code and allows you to use the really great error handling capabilities of promises in your control flow.

      请求这些项目,它看起来像是构造一个在URL中占用大量参数的URL,并将结果返回给JSON。我假设这是一个node.js环境。所以,以下是使用node.js request()模块执行 getItems()的方法。这将返回一个承诺,其解析的值将是已经解析的Javascript对象,表示api调用的结果。

      For requesting the items, it looks like you construct a URL that takes a bunch of arguments in the URL and you get the results back in JSON. I'm assuming this is a node.js environment. So, here's how you could do the getItems() implementation using the node.js request() module. This returns a promise whose resolved value will be the already parsed Javascript object representing the results of the api call.

      function getItems(room, start, qty, token) {
          return new Promise(function(resolve, reject) {
              var url = 'https://api.gitter.im/v1/rooms/' + room + '/chatMessages' + '?access_token=' + token + '&limit=' + qty + '&skip=' + start;
              request({url: url, json: true}, function(err, msg, result) {
                  if (err) return reject(err);
                  resolve(result);
              });
          });
      }    
      

      为了存储项目,我们要完成同样的事情。我们要创建一个函数,它将数据存储为参数并返回一个承诺,并且在函数内部执行所有的脏工作。所以逻辑上你想要有这样的结构:

      For storing the items, we want to accomplish the same thing. We want to create a function that takes the data to store as arguments and returns a promise and it does all the dirty work inside the function. So logically, you want to have this structure:

      function storeItems(data) {
          return new Promise(function(resolve, reject) {
              // do the actual database operations here
              // call resolve() or reject(err) when done
          });
      }
      

      对不起,但我不太明白你在做什么mySql数据库足以充分填写这个功能。希望这个结构给你足够的想法如何完成它或者问一些问题,如果你卡住了。

      Sorry, but I don't quite understand what you're doing with your mySql database enough to fully fill out this function. Hopefully this structure gives you enough of an idea how to finish it off or ask some questions if you get stuck.

      注意:如果您使用像Bluebird这样的Promise库来添加额外的承诺功能,那么承诺现有的操作是Bluebird内置的,所以 getItems()就是这样: / p>

      Note: if you use a Promise library like Bluebird to add additional promise functional functionality, then promisifying an existing operation is something that is built into Bluebird so getItems() just becomes this:

      var Promise = require('bluebird');
      var request = Promise.promisifyAll(require('request'));
      
      function getItems(room, start, qty, token) {
          var url = 'https://api.gitter.im/v1/rooms/' + room + '/chatMessages' + '?access_token=' + token + '&limit=' + qty + '&skip=' + start;
          return request.getAsync({url: url, json: true});
      }    
      

      这篇关于在递归中使用javascript承诺进行Api调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆