首页 > 解决方案 > 异步循环不尊重异步

问题描述

我有点卡在异步功能上。

我要完成的工作- 我正在创建一个批处理函数(batchGetSubs),它将遍历一组文件,读取一个 ID,然后发出 API 请求,等待响应(THE ISSUE),然后写入一个新的带有格式化数据的文件。

问题- 我已经尝试了 Async 和 Await,以及推送承诺并尝试使用 Promise.all 来等待承诺得到解决,但没有成功。当前的行为是,在 API 调用实际返回所有数据之前,我获得了 Promise.all 部分中的所有 console.logs。我使用这些文章作为参考:

守则——

async function batchGetSubs(data, command) {
    console.time('batchGetSubs')
    iteration = 1;
    let dataArray = []; promises = [];

    // LOOP THROUGH FILES, THEN GET TENANTS BY SUB
    for (i = iteration; i < totalIterations; i++) {
        let msIds = await loopAndDump(iteration);

        // LOOP THROUGH TENANTIDS AND GET SUBSCRIPTIONS
        msIds.map(async item => {
            let newRecord = await getSubsByTenantId(item);
            promises.push(await newRecord);
        });
        }
        Promise.all([promises]).then(() => { // FIXME: WHY IS THIS NOT WAITING FOR ALL RESPONSES?
            console.log(p1SubscriptionArray),
            console.timeEnd('batchGetSubs')
        });
    }



async function getSubsByTenantId(msTenantId) {
    let newRecord; let subscriptionArray = [];
    let bearerToken = p1BearerToken;
    let totalSubs = 0;
    const subIdConfig = {
        method: 'get',
        url: ``,
        headers: { 'Authorization': bearerToken }
    }
    await delay();
    await axios(subIdConfig)
        .then(async res => {
            console.log('AXIOS RESPONSE', res.data);
            if (res.data.items) {
                let subItems = res.data.items;
                console.log('^^^^^^^^^^^^^^^^^^', res.data.items)
                // LOOP THROUGH AND ADD TO SUBSCRIPTION ARRAY
                subItems.map(async subscription => {
                    if (subscription.friendlyName) {
                        totalSubs++;
                        subscriptionArray.push(subscription.friendlyName);
                    }
                });
                newRecord = { "tenantId": msTenantId, "totalSubs": totalSubs, "subscriptionFriendlyName": subscriptionArray };
            } else {
                // NO SUBS
                newRecord = { "tenantId": msTenantId, "totalSubs": totalSubs, "subscriptionFriendlyName": ['NONE'] };
                let statusCode, errorMessage;
                if (error && error.response === undefined) { // GETTING STATUS -4039 or -4077 INSTEAD OF 429 WHEN CHECKING SUBS. FORCE A RESEND.
                    if (error.errno && error.errno === -4039 || error.errno && error.errno === -4077) statusCode = 429; errorMessage = error.code;
                } else {
                    statusCode = error.response.status;
                    errorMessage = error.response.statusText;
                }
                console.error('ERROR:: SUBIDCONFIG SECTION THROWING ERROR: ', statusCode, portal, errorMessage);
                // SORT NON-200 CALLS BASED ON STATUS CODE
                switch (statusCode) {
                    case 403:
                        status403.push('subs', newRecord);
                        break;
                    case 404:
                        status404.push('subs', newRecord);
                        erroring = true;
                        break;
                    case 429:
                        status429.push('subs', newRecord);
                        erroring = true;
                        break;
                    default:
                        statusOther.push('subs', newRecord)
                        erroring = true;
                        break;
                }
            }
        })
        .catch(err => {
            newRecord = { "tenantId": msTenantId, "totalSubs": totalSubs, "subscriptionFriendlyName": ['NONE'] };
            console.error('ERROR: REQUEST IN GETSUBSBYTENANTID(): ', err)
        })
        .then(res => {
            console.log('DO WE HAVE ANY INFORMATION? ', newRecord);
            p1SubscriptionArray.push(newRecord);
            resolve();
        });
}

标签: javascriptnode.jsloopsasynchronouspromise

解决方案


我只检查了您提出问题的第一个功能:

为什么不等待所有响应?

有几个原因:

  1. promise调用时数组仍然为空Promise.all。这是因为您只在 apush 之后执行a await,因此这是push异步发生的(阅读:稍后)。

  2. 即使promises数组被填充,它也不会有 Promise 对象,而是解析值(即newRecord值)

  3. 即使promises本来是一组承诺,你也没有将该数组正确传递给Promise.all:你将该数组包装在另一个数组中,然后该数组只有一个条目,并且该条目不是一个承诺,而是一个数组。

与您的问题无关,但是:

  • 请养成明确声明所有变量的习惯。您没有为iteration,promises或执行此操作i

  • .map在您对返回值执行某些操作时使用。对于纯迭代使用.forEachor for。在这种情况下,您可以使用返回值来扩展promises数组。

  • 如果您打算调用batchGetSubs并需要知道所有操作何时完成,请确保它返回一个有用的承诺:return Promise.all()

这是该功能的建议更正:

async function batchGetSubs(data, command) {
    console.time('batchGetSubs')
    let iteration = 1; // Declare!
    let dataArray = []; 
    let promises = []; // declare

    // declare i
    for (let i = iteration; i < totalIterations; i++) {
        let msIds = await loopAndDump(iteration);

        // Use the return value of the map method. No need for async callback
        promises.push(...msIds.map(item => {
            // Get the promise, not the resolved value, as that will come too late:
            return getSubsByTenantId(item);
        }));
    }
    // promises is already an array; don't make it an array of arrays.
    // And: return the resulting promise: it may be useful to the caller.
    return Promise.all(promises).then(() =>  {
        console.log(p1SubscriptionArray),
        console.timeEnd('batchGetSubs')
    });
}

推荐阅读