Javascript 异步混乱。正在处理一个API响应,并对代码中发生的事情失去了理解

Javascript 异步混乱。正在处理一个API响应,并对代码中发生的事情失去了理解,javascript,node.js,asynchronous,Javascript,Node.js,Asynchronous,关于如何最好地构造这个函数,我在自己的头脑中迷失了方向。 下面的代码基本上是正确的,我只是在底部删掉了一些函数,并不重要 应该发生什么: 我的节点应用程序在特定时间运行并调用refreshDevices() 该函数从API获取JSON负载 该函数在该有效负载中的元素上循环,并检查是否已经存在记录 如果没有记录,则创建一个记录,如果有,则更新该记录 运行此操作后,将创建一个日志条目,记录更改的数量 我的问题是日志条目发生在任何记录检查之前。。。我的日志显示0已添加和0已更新。 我尝试重新编写代码以

关于如何最好地构造这个函数,我在自己的头脑中迷失了方向。 下面的代码基本上是正确的,我只是在底部删掉了一些函数,并不重要

应该发生什么:

  • 我的节点应用程序在特定时间运行并调用
    refreshDevices()
  • 该函数从API获取JSON负载
  • 该函数在该有效负载中的元素上循环,并检查是否已经存在记录
  • 如果没有记录,则创建一个记录,如果有,则更新该记录
  • 运行此操作后,将创建一个日志条目,记录更改的数量
  • 我的问题是日志条目发生在任何记录检查之前。。。我的日志显示0已添加和0已更新。 我尝试重新编写代码以使用内联异步/await函数,但我认为这实际上与下面相同

    我应该如何构造这个函数来实现我所说的。 我发现异步和同步的混合让我很困惑

    • 我甚至尝试在
      .finally()
      块,但这是在检查发生之前执行的
    • 我使用了
      forEach
      map
      两种方法尝试了这一点

    这个问题

    您可以使用
    Async/await
    使代码更具可读性,并使用
    for of
    对数据进行迭代

    for of
    循环将等待第一次迭代完成,然后进行第二次迭代,依此类推

    另一方面,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
    }
    

    正如您所确定的,您的
    system.log
    调用在启动一系列尚未解决的承诺后立即发生

    refreshDevices(\u 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(JSON.map(…)。然后(=>systemlog.log)(…)您可能想考虑将VAR <代码> JSON < /代码>重命名为<代码>数据< /代码>或类似的内容,以免混淆JS。object@csum说得好,我没想到。谢谢!@kaido这听起来像Ellight在下面回答的。我要试试看!这应该行得通,尽管你正在启动多个异步进程将多个同步进程转换为多个同步进程,而不是允许每个承诺异步运行,然后在最终承诺解决后立即继续执行。感谢@Sohail,这看起来就像我试图做的那样(我对该函数重新编码了大约5次,我有一个版本具有async/Wait everywhere)。但是,我没有使用
    for of
    循环,这听起来像是我的撤销。我今天学到了有关forEach&map函数的一些新知识…谢谢!谢谢!这看起来是一条路要走,我以后会尝试更新我的代码。我仍然在努力承诺将函数从堆栈中拉出,并按照顺序执行r、 感谢你的回答:D
    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(promises).then(
                systemlog.log({
                    message: 'Refresh',
                    data: {
                        success: true,
                        note: `Added: ${status.added} & updated: ${status.updated}`,
                    },
                });
            )