Вызов Api с помощью обещаний javascript в рекурсии
Я хотел бы использовать Gitter api, чтобы получить все сообщения из комнаты.
Что мне нужно сделать, так это отправить запрос get api, например, с 50 элементами, onComplete мне нужно отправить другой запрос с 50 элементами и пропустить 50 элементов, которые я уже получил. Делайте этот запрос до тех пор, пока они не вернут какие-либо предметы. Итак:
- Отправить запрос api
- json разобрать его
- : запрос содержит элементы
- Сделайте sql-запрос с этими элементами
- продолжить запрос
- Отправить следующий api запрос (рекурсия?)
- ? если в следующем запросе api больше нет элементов-показать сообщение done
- : запрос не содержит элементов
- прервать сообщение
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);
Может быть, вы можете проверить и помочь мне? Или предоставить аналогичный код для таких вещи?
Обновление
Вот до чего я переработал код: jsfiddle
1 ответ:
Ваш код меня сильно запутал. Самый простой способ использовать обещания с асинхронными операциями - "обещать" существующие асинхронные операции, а затем написать всю логику после этого, используя обещания. "Обещать" что-то означает генерировать или писать функцию-оболочку, которая возвращает обещание, а не использует только обратный вызов.
Во-первых, давайте рассмотрим общую логику. В соответствии с вашим вопросом, Вы сказали, что у вас есть API, который вы хотите вызвать, чтобы получить 50 элементов одновременно, пока вы не получите их всех. Это можно сделать с помощью рекурсивной структуры. Создайте внутреннюю функцию, которая выполняет извлечение и возвращает обещание, и каждый раз, когда она завершается, вызывайте ее снова. Предположим, что здесь задействованы две основные функции: одна с именемgetItems()
, которая получает элементы из вашего API и возвращает обещание, и другая с именемstoreItems()
, которая хранит эти элементы в вашей базе данных.Этот код использует цепочку обещаний, которая является немного продвинутой, но чрезвычайно полезной функцией обещаний. Когда вы возвращаете обещание от обработчика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()
, оно привязывается к предыдущему обещанию, автоматически связывая их все вместе в серию операций. Конечный результат возврата или ошибка затем возвращаются обратно через исходное обещание исходному вызывающему объекту. Аналогично, любая ошибка, которая может произойти в этой цепочке, передается обратно к исходному вызывающему объекту. Это чрезвычайно полезно в сложных функциях с несколькими асинхронными операциями, где вы не можете просто вернуть или бросьте, если используете регулярные обратные вызовы.Тогда это будет называться так:
Теперь я поработаю над некоторыми примерами для созданных многообещающих версий ваших вызовов нижних уровней для реализацииgetAllItems(this.room, 50, config.token).then(function() { // finished successfully here }, function(err) { // had an error here });
getItems()
иstoreItems()
. Сейчас вернусь с ними.Я не вполне понимаю все детали ваших асинхронных операций, поэтому это не будет полностью рабочий пример, но должен проиллюстрировать общую концепцию, и вы можете задать любые необходимые уточняющие вопросы о реализация.
При обещании функции главное, что вы хотите сделать, - это инкапсулировать грязную работу обработки обратного вызова и условий ошибки в одну основную функцию, которая возвращает обещание. Это затем позволяет использовать эту функцию в хорошем чистом потоковом коде на основе обещаний и позволяет использовать действительно большие возможности обработки ошибок обещаний в потоке управления.
Для запроса элементов, похоже, вы создаете URL-адрес, который занимает кучу аргументы в URL-адресе, и вы получите результаты обратно в JSON. Я предполагаю, что это узел.JS окружающая среда. Итак, вот как вы можете выполнить реализацию
getItems()
с помощью узла.модуль jsrequest()
. Это возвращает обещание, разрешенное значение которого будет уже проанализированным объектом Javascript, представляющим результаты вызова api.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); }); }); }
Для хранения предметов мы хотим сделать то же самое. Мы хотим создать функцию, которая принимает данные для хранения в качестве аргументов и возвращает обещание и он делает всю грязную работу внутри функции. Итак, логически вы хотите иметь такую структуру:
function storeItems(data) { return new Promise(function(resolve, reject) { // do the actual database operations here // call resolve() or reject(err) when done }); }
Извините, но я не совсем понимаю, что вы делаете с вашей базой данных mySql достаточно, чтобы полностью заполнить эту функцию. Надеюсь, эта структура даст вам достаточно идей, как закончить его или задать несколько вопросов, если вы застряли.
Примечание: Если вы используете библиотеку Promise, такую как Bluebird, чтобы добавить дополнительную функциональную функциональность promise, то promisifying существующая операция-это то, что встроено в Bluebird, поэтому
getItems()
просто становится таким: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}); }