Асинхронная путаница. Обработка ответа API и потеря моего гр asp на том, что происходит в этом коде - PullRequest
2 голосов
/ 04 марта 2020

Я заблудился в собственной голове о том, как лучше структурировать эту функцию. Приведенный ниже код в основном корректен, я просто остановил некоторые функции внизу, не важно.

Что должно произойти:

  1. Мое приложение узла работает на определенное время и вызовы refreshDevices()
  2. Функция извлекает полезную нагрузку JSON из API
  3. Функция перебирает элементы в этой полезной нагрузке и проверяет, существует ли уже запись
  4. Если записи нет, она создается, если есть, то она обновляется
  5. После запуска создается запись в журнале, регистрирующая количество изменений.

My проблема в том, что запись в журнале происходит до начала какой-либо проверки записей ... мой журнал показывает 0 добавлено и 0 обновлено. Я пытался переписать код, чтобы использовать встроенную функцию асинхронности / ожидания, но я думаю, что это фактически то же самое, что и ниже.

Как я должен структурировать эту функцию, чтобы сделать то, что я сказал , Я нахожу смесь asyn c и syn c очень запутанной, чтобы обернуть мою голову.

  • Я даже попытался добавить бит записи журнала, чтобы это произошло в блоке .finally() в конец, но это выполнялось до того, как проверки произошли.
  • Я пробовал это, используя forEach и map.
refreshDevices(_timespan) {  
  const status = {
    added: 0,
    updated: 0,
  };

  // Standard API fetch & json() conversion
  fetch(`https://api.example.com/test&timespan=${timespan}`)
    .then((response) => response.json())
    .then((JSON) => {
      JSON.forEach((device) => {
        findDevice(device._id)
        .then(record => {
          if (!record) {
            addDeviceRecord(device);
            status.added++;
          } else {
            deviceSeen(device._id);
            status.updated++;
          }
        })
        .catch(error => {
          console.log('Error!');
        });
      });

      systemlog.log({
        message: 'Refresh',
        data: {
          success: true,
          note: `Added: ${status.added} & updated: ${status.updated}`,
        },
      });
    })
    .catch((error) => {
      systemlog.log({
        message: 'Refresh',
        type: systemlog.logType.ERROR,
        data: {
          success: false,
          note: error,
        },
      });
    });
}

function findDevice(id) {
  return DB('device').findOne({ id }); // this returns is a promise
}

function addDeviceRecord(record) {
  return DB('device').insertOne(record); // this returns is a promise
}

function deviceSeen(id) {
  return DB('device').updateOne({ id }, { $set: { lastSeen: new Date() } }); // this returns is a promise
}

Этот вопрос

Ответы [ 2 ]

1 голос
/ 04 марта 2020

Как вы определили, ваш system.log вызов происходит сразу после запуска списка обещаний, которые не были решены.

Внутри вашей функции refreshDevices(_timespan) добавьте пустой массив, чтобы добавить каждое новое обещание в forEach.

Затем измените ваш код следующим образом:

JSON.forEach((device) => {
            promises.push( // Add this promise to the array
                findDevice(device._id)
                .then(record => {
                    if (!record) {
                        addDeviceRecord(device);
                        status.added++;
                    } else {
                        deviceSeen(device._id);
                        status.updated++;
                    }
                })
                .catch(error => {
                    console.log('Error!');
                });
            ) // end of .push() arg
        });

Затем используйте Promise.all (), чтобы дождаться разрешения всех обещаний перед вызовом systemLog.log, что-то вроде (псевдо) -псевдо-код:)

Promise.all(promises).then(
            systemlog.log({
                message: 'Refresh',
                data: {
                    success: true,
                    note: `Added: ${status.added} & updated: ${status.updated}`,
                },
            });
        )
1 голос
/ 04 марта 2020

Вы можете использовать Async/await, чтобы сделать код более читабельным, и использовать for of для итерации по данным.

for of l oop будет ожидать завершения первой итерации, а затем go для второго и так далее.

С другой стороны, forEach основан на обратном вызове, поэтому он будет выполнять и регистрировать обратный вызов для каждой итерации в очереди обратных вызовов и выходить из него, не дожидаясь завершения обратных вызовов.

Попробуйте это

    async refreshDevices(_timespan) {
        const status = {
            added: 0,
            updated: 0,
        };
        // Standard API fetch & json() conversion
        try {
            let response = await fetch(`https://api.example.com/test&timespan=${timespan}`)
            let data= await response.json();
            for (const device of data) {
                try {
                    let record = await findDevice(device._id);
                    if (!record) {
                        addDeviceRecord(device);
                        status.added++;
                    } else {
                        deviceSeen(device._id);
                        status.updated++;
                    }
                } catch (error) {
                    console.log('Error!');
                }
            }
            systemlog.log({
                message: 'Refresh',
                data: {
                    success: true,
                    note: `Added: ${status.added} & updated: ${status.updated}`,
                },
            });
        } catch (error) {
            systemlog.log({
                message: 'Refresh',
                type: systemlog.logType.ERROR,
                data: {
                    success: false,
                    note: error,
                },
            });
        }
    }

function findDevice(id) {
    return DB('device').findOne({ id }); // this returns is a promise
}
function addDeviceRecord(record) {
    return DB('device').insertOne(record); // this returns is a promise
}
function deviceSeen(id) {
    return DB('device').updateOne({ id }, { $set: { lastSeen: new Date() } }); // this returns is a promise
}

В качестве альтернативы вы также можете использовать promise.all, чтобы дождаться выполнения всех обещаний.

 async refreshDevices(_timespan) {
        const status = {
            added: 0,
            updated: 0,
        };
        // Standard API fetch & json() conversion
        try {
            let response = await fetch(`https://api.example.com/test&timespan=${timespan}`)
            let data = await response.json();
            let promises = data.map(device => {
                return new Promise( async (resolve, reject) => {
                    try {
                        let record = await findDevice(device._id);
                        if (!record) {
                            addDeviceRecord(device);
                            status.added++;
                        } else {
                            deviceSeen(device._id);
                            status.updated++;
                        }
                        resolve();
                    } catch (error) {
                        console.log('Error!');
                        reject(error);
                    }
                });
            });
            let result = await Promise.all(promises);
            systemlog.log({
                message: 'Refresh',
                data: {
                    success: true,
                    note: `Added: ${status.added} & updated: ${status.updated}`,
                },
            });
        } catch (error) {
            systemlog.log({
                message: 'Refresh',
                type: systemlog.logType.ERROR,
                data: {
                    success: false,
                    note: error,
                },
            });
        }
    }
function findDevice(id) {
    return DB('device').findOne({ id }); // this returns is a promise
}
function addDeviceRecord(record) {
    return DB('device').insertOne(record); // this returns is a promise
}
function deviceSeen(id) {
    return DB('device').updateOne({ id }, { $set: { lastSeen: new Date() } }); // this returns is a promise
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...