Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/418.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
嵌套Javascript承诺-从firestore获取数据_Javascript_Node.js_Firebase_Google Cloud Firestore_Promise - Fatal编程技术网

嵌套Javascript承诺-从firestore获取数据

嵌套Javascript承诺-从firestore获取数据,javascript,node.js,firebase,google-cloud-firestore,promise,Javascript,Node.js,Firebase,Google Cloud Firestore,Promise,在过去的3天里,我一直被这个错误困扰着,我尝试了一切,尝试了1000种方式来构建承诺,但似乎没有任何效果。也许我失去了“大局”,所以希望新的眼睛会有所帮助。感谢阅读: 我有一个在Firebase云函数中运行的预定函数。代码试图实现的是 检查文档是否过期并将其更改为“非活动”>>此部分有效 如果某个文档被设置为非活动,我想查看firestore数据库中是否有任何其他相同“类型”的文档。如果没有其他相同类型的文档,那么我想从我的文档“类型”中删除该类型 在我最近的尝试(复制如下)中,我检查快照中是否

在过去的3天里,我一直被这个错误困扰着,我尝试了一切,尝试了1000种方式来构建承诺,但似乎没有任何效果。也许我失去了“大局”,所以希望新的眼睛会有所帮助。感谢阅读:

我有一个在Firebase云函数中运行的预定函数。代码试图实现的是

  • 检查文档是否过期并将其更改为“非活动”>>此部分有效
  • 如果某个文档被设置为非活动,我想查看firestore数据库中是否有任何其他相同“类型”的文档。如果没有其他相同类型的文档,那么我想从我的文档“类型”中删除该类型
  • 在我最近的尝试(复制如下)中,我检查快照中是否有文档(这意味着有另一个相同类型的文档,因此不必删除该文档)。那么如果res!==是的,我会删除该文档

    问题是,由于某种原因,res永远都不是真的。。。。也许“res”承诺在“snapshot”承诺之前就解决了

    const functions=require('firebase-functions');
    const admin=require('firebase-admin');
    admin.initializeApp();
    exports.scheduledFunction=functions.pubsub
    .时间表('0 23***')。时区('欧洲/马德里')
    .onRun(异步(上下文)=>{
    const expiredDocs=await admin.firestore()集合('PROMOTIONS\u INFO')
    .where('active','=',true)
    
    .where('expiration',“在这种情况下,我假设
    res
    未定义,并且评估为false

    在您的
    。然后
    承诺使用
    res
    参数之前,您有一个先前的
    。然后
    承诺返回无效:

    //...
    .then((snapshot) => {
        snapshot.docs.map((doc)=>{return true}) // <--- This is not actually returning a resolved value
    }).then(async (res) => {
        res===true ? null : 
        (await admin.firestore().collection('PROMOTIONS_INFO').doc('types')
        .update('types', admin.firestore.FieldValue.arrayRemove(type)))
    })
    //...
    
    snapshot.docs.map((doc)=>{return true})
    返回类似于
    [true,false]的数组
    而不是类似于布尔值的
    true

    所以
    .then(async(res)=>{res==true?null:await admin.firestore(…
    无法工作)。 及

    也许你应该修改如下

    。然后((快照)=>
    snapshot.docs.length>0?空:
    等待firestore管理员(。。。
    
    <>代码> 为了实现您所期望的结果,您可能需要考虑使用和拆分代码到不同的步骤。

    一组可能的步骤是:

  • 获取所有仍处于活动状态的过期文档
  • 没有过期文档?日志结果和结束功能
  • 对于每个过期文件:
    • 将其更新为非活动
    • 存储它的类型以便稍后检查
  • 对于要检查的每个类型,请检查具有该类型的活动文档是否存在,如果不存在,请存储该类型以稍后删除
  • 没有要删除的类型?日志结果和结束函数
  • 删除所有需要删除的类型
  • 日志结果和结束函数
  • 在上面的步骤中,步骤3可以使用,步骤6可以使用可以移除的来减轻数据库的负担

    
    const functions = require('firebase-functions');
    const admin = require('firebase-admin');
    admin.initializeApp();
    
    exports.scheduledFunction = functions.pubsub
    .schedule('0 23 * * *').timeZone('Europe/Madrid')
    .onRun( async (context) => {
        // get instance of Firestore to use below
        const db = admin.firestore();
        
        // this is reused often, so initialize it once.
        const promotionsInfoColRef = db.collection('PROMOTIONS_INFO');
    
        // find all documents that are active and have expired.
        const expiredDocsQuerySnapshot = await promotionsInfoColRef
            .where('active','==',true)
            .where('expiration', '<=', new Date())
            .get();
    
        if (expiredDocsQuerySnapshot.empty) {
            // no expired documents, log the result
            console.log(`No documents have expired recently.`);
            return; // done
        } 
        
        // initialize an object to store all the types to be checked
        // this helps ensure each type is checked only once
        const typesToCheckObj = {};
        
        // initialize a batched write to make changes all at once, rather than call out to Firestore multiple times
        // note: batches are limited to 500 read/write operations in a single batch
        const makeDocsInactiveBatch = db.batch();
        
        // for each snapshot, add their type to typesToCheckObj and update them to inactive
        expiredDocsQuerySnapshot.forEach(doc => {
            const type = doc.get("business.type"); // rather than use data(), parse only the property you need.
            typesToCheckObj[type] = true; // add this type to the ones to check
            makeDocsInactiveBatch.update(doc.ref, { active: false }); // add the "update to inactive" operation to the batch
        });
        
        // update database for all the now inactive documents all at once.
        // we update these documents first, so that the type check are done against actual "active" documents.
        await makeDocsInactiveBatch.commit();
        
        // this is a unique array of the types encountered above
        // this can now be used to check each type ONCE, instead of multiple times
        const typesToCheckArray = Object.keys(typesToCheckObj);
        
        // check each type and return types that have no active promotions
        const typesToRemoveArray = (await Promise.all(
            typesToCheckArray.map((type) => {
                return promotionsInfoColRef
                    .where('active','==',true)
                    .where('business.type','==', type)
                    .limit(1)
                    .get()
                    .then((querySnapshot) => querySnapshot.empty ? type : null) // if empty, include the type for removal
            })
        ))
        .filter((type) => type !== null); // filter out the null values that represent types that don't need removal
        
        // typesToRemoveArray is now a unique list of strings, containing each type that needs to be removed
        
        if (typesToRemoveArray.length == 0) {
            // no types need removing, log the result
            console.log(`Updated ${expiredDocsQuerySnapshot.size} expired documents to "inactive" and none of the ${typesToCheckArray.length} unique types encountered needed to be removed.`);
            return; // done
        }
        
        // get the types document reference
        const typesDocRef = promotionsInfoColRef.doc('types');
    
        // use the arrayRemove field transform to remove all the given types at once
        await typesDocRef.update({types: admin.firestore.FieldValue.arrayRemove(...typesToRemoveArray) });
    
        // log the result
        console.log(`Updated ${expiredDocsQuerySnapshot.size} expired documents to "inactive" and ${typesToRemoveArray.length}/${typesToCheckArray.length} unique types encountered needed to be removed.\n\nThe types removed: ${typesToRemoveArray.sort().join(", ")}`);
    

    要使用此功能,请将原始代码中的
    db.batch()
    替换为
    new MultiBatch(db)
    。如果批中的更新(如
    someBatch.update(ref,{…})
    )包含字段转换(如
    FieldValue.arrayRemove()
    ),请确保使用
    someMultiBatch.transformUpdate(ref,{…})
    相反,这样一次更新就可以正确地算作两次操作(读和写)。

    如果要使用
    ()=>{/*code*/}
    风格的箭头函数,则需要
    从所有函数返回
    内容。还有一个“末日金字塔”在这里开发承诺和异步/等待意味着消除。考虑是否可以等待<代码> ExpDeCords<代码>集合>结束>代码>。
    。然后等待
    存在
    集合。然后循环该集合并对每个值运行更新。它起作用了!我非常感谢您的时间和全面的回答。我想,鉴于我们有typesToRemoveArray,也许可以通过读取Types文档,合并t他在代码中添加了两个数组,然后将其写入数据库。这会对多批处理方法产生影响吗?另外,您对运行时间有什么考虑吗?@Mireia您说得很对。今天以全新的眼光重新审视代码时,我记得
    arrayRemove
    一次支持多个元素-当以s单独的参数-可以使用排列(
    )实现运算符。这完全消除了第二个批处理操作。就运行时间而言,上面的代码可能是您能够实现的最精简的代码。最慢的部分将是在有大量数据要检查时检查类型是否仍然存在,但检查每种类型一次有助于保持较低的性能。如果启动t由于更新时间超过60秒而超时,您可以将更新时间增加到9分钟,或者一天运行多次(后者是更好的选择)。只需将
    0 23***
    更改为
    0 5,11,17,23***
    即可每6小时运行一次。这太棒了!谢谢。出于好奇,您知道对多个元素使用arrayRemove是否算作1读写或1像素?
    //...
    .then((snapshot) => {
        return snapshot.docs.map((doc)=>{return true})
    }).then(async (res) => {
        res===true ? null : 
        (await admin.firestore().collection('PROMOTIONS_INFO').doc('types')
        .update('types', admin.firestore.FieldValue.arrayRemove(type)))
    })
    //...
    
    
    const functions = require('firebase-functions');
    const admin = require('firebase-admin');
    admin.initializeApp();
    
    exports.scheduledFunction = functions.pubsub
    .schedule('0 23 * * *').timeZone('Europe/Madrid')
    .onRun( async (context) => {
        // get instance of Firestore to use below
        const db = admin.firestore();
        
        // this is reused often, so initialize it once.
        const promotionsInfoColRef = db.collection('PROMOTIONS_INFO');
    
        // find all documents that are active and have expired.
        const expiredDocsQuerySnapshot = await promotionsInfoColRef
            .where('active','==',true)
            .where('expiration', '<=', new Date())
            .get();
    
        if (expiredDocsQuerySnapshot.empty) {
            // no expired documents, log the result
            console.log(`No documents have expired recently.`);
            return; // done
        } 
        
        // initialize an object to store all the types to be checked
        // this helps ensure each type is checked only once
        const typesToCheckObj = {};
        
        // initialize a batched write to make changes all at once, rather than call out to Firestore multiple times
        // note: batches are limited to 500 read/write operations in a single batch
        const makeDocsInactiveBatch = db.batch();
        
        // for each snapshot, add their type to typesToCheckObj and update them to inactive
        expiredDocsQuerySnapshot.forEach(doc => {
            const type = doc.get("business.type"); // rather than use data(), parse only the property you need.
            typesToCheckObj[type] = true; // add this type to the ones to check
            makeDocsInactiveBatch.update(doc.ref, { active: false }); // add the "update to inactive" operation to the batch
        });
        
        // update database for all the now inactive documents all at once.
        // we update these documents first, so that the type check are done against actual "active" documents.
        await makeDocsInactiveBatch.commit();
        
        // this is a unique array of the types encountered above
        // this can now be used to check each type ONCE, instead of multiple times
        const typesToCheckArray = Object.keys(typesToCheckObj);
        
        // check each type and return types that have no active promotions
        const typesToRemoveArray = (await Promise.all(
            typesToCheckArray.map((type) => {
                return promotionsInfoColRef
                    .where('active','==',true)
                    .where('business.type','==', type)
                    .limit(1)
                    .get()
                    .then((querySnapshot) => querySnapshot.empty ? type : null) // if empty, include the type for removal
            })
        ))
        .filter((type) => type !== null); // filter out the null values that represent types that don't need removal
        
        // typesToRemoveArray is now a unique list of strings, containing each type that needs to be removed
        
        if (typesToRemoveArray.length == 0) {
            // no types need removing, log the result
            console.log(`Updated ${expiredDocsQuerySnapshot.size} expired documents to "inactive" and none of the ${typesToCheckArray.length} unique types encountered needed to be removed.`);
            return; // done
        }
        
        // get the types document reference
        const typesDocRef = promotionsInfoColRef.doc('types');
    
        // use the arrayRemove field transform to remove all the given types at once
        await typesDocRef.update({types: admin.firestore.FieldValue.arrayRemove(...typesToRemoveArray) });
    
        // log the result
        console.log(`Updated ${expiredDocsQuerySnapshot.size} expired documents to "inactive" and ${typesToRemoveArray.length}/${typesToCheckArray.length} unique types encountered needed to be removed.\n\nThe types removed: ${typesToRemoveArray.sort().join(", ")}`);
    
    class MultiBatch {
        constructor(dbRef) {
            this.dbRef = dbRef;
            this.batchOperations = [];
            this.batches = [this.dbRef.batch()];
            this.currentBatch = this.batches[0];
            this.currentBatchOpCount = 0;
            this.committed = false;
        }
        
        /** Used when for basic update operations */
        update(ref, changesObj) {
            if (this.committed) throw new Error('MultiBatch already committed.');
            if (this.currentBatchOpCount + 1 > 500) {
                // operation limit exceeded, start a new batch
                this.currentBatch = this.dbRef.batch();
                this.currentBatchOpCount = 0;
                this.batches.push(this.currentBatch);
            }
            this.currentBatch.update(ref, changesObj);
            this.currentBatchOpCount++;
        }
        
        /** Used when an update contains serverTimestamp, arrayUnion, arrayRemove, increment or decrement (which all need to be counted as 2 operations) */
        transformUpdate(ref, changesObj) {
            if (this.committed) throw new Error('MultiBatch already committed.');
            if (this.currentBatchOpCount + 2 > 500) {
                // operation limit exceeded, start a new batch
                this.currentBatch = this.dbRef.batch();
                this.currentBatchOpCount = 0;
                this.batches.push(this.currentBatch);
            }
            this.currentBatch.update(ref, changesObj);
            this.currentBatchOpCount += 2;
        }
        
        commit() {
            this.committed = true;
            return Promise.all(this.batches.map(batch => batch.commit()));
        }
    }