在 forEach 中使用 Firebase/Firestore 异步/等待

时间:2021-03-23 21:13:04

标签: javascript firebase asynchronous google-cloud-firestore

我正在尝试使用 getL 函数动态设置 HTTP 响应的属性,但它给了我一个 Promise 并且我无法在 async 上设置 forEach,因为那打破它(见 generateLinksForList 函数)。还有其他方法可以做到这一点吗?

/**
 * @module domain/helper
 */
const { 
  complement, 
  compose, 
  isNil, 
  pickBy 
} = require('ramda');
const notNull = compose(complement(isNil));

/**
 * @function cleanData
 * @returns {undefined}
 */
const cleanData = (entity) => pickBy(notNull, entity);

/**
 * @name generateRelations
 * @returns {undefined}
 */
const generateRelations = () => ([]);

async function getL(repo, entityContext) {
  let d = await repo.nextItem(entityContext);
  console.log(d)
  return d;
}

/**
 * @name generateLinksForList
 * @returns {undefined}
 */
const generateLinksForList = (entityContext, type, entityName, repo) => {

  const relations = require(`./${entityName}/relations.js`)
  const namespace = type + 'Relations';
  const _relations = relations[namespace];
  let host = 'http://localhost:4000';
  let relationsList = [];
  _relations.forEach((linkRelation) => {
    Object.keys(linkRelation).forEach((key) => {
      if (linkRelation.hasOwnProperty('next')) {
        let relation = {};
        let l = getL(repo, entityContext);
        console.log('---- getL');
        console.log(l);
        if (l.length) {
          relation.next = linkRelation.next
            .replace('{nextId}', l[0].id)
        }
        relation.next = linkRelation.next
          .replace('{fullhost}', host)
          .replace('{id}', entityContext.id);
        relationsList.push(relation);
      }
      if (linkRelation.hasOwnProperty('self')) {
        let relation = {};
        relation.self = linkRelation['self']
          .replace('{fullhost}', host)
          .replace('{id}', entityContext.id);
        relationsList.push(relation);
      }
    })
  });

  return relationsList;
};

/**
 * @function generateLinksForItem
 * @static
 * @returns {array}
 */
const generateLinksForItem = (entityContext, type, entityName) => {
  const relations = require(`./${entityName}/relations.js`)
  const namespace = type + 'Relations';
  const _relations = relations[namespace];
  let host = 'http://localhost:4000';
  let token, eventToken;
  let _list = [];
  _relations.forEach((relRef) => {
    Object.keys(relRef).forEach((keyRef) => {
      if (relRef[keyRef].includes('{id}')) {
        relRef[keyRef] = relRef[keyRef].replace(/{id}/, entityContext.id);
      }
      if (relRef[keyRef].includes('{fullhost}')) {
        relRef[keyRef] = relRef[keyRef].replace(/{fullhost}/, host);
      }
      if (relRef[keyRef].includes('{token}')) {
        relRef[keyRef] = relRef[keyRef].replace(/{token}/, token);
      }
      if (relRef[keyRef].includes('{eventToken}')) {
        relRef[keyRef] = relRef[keyRef].replace(/{eventToken}/, eventToken);
      }
      _list.push({
        rel: keyRef,
        href: relRef[keyRef]
      });
    });
  });
  return _list;
};

/**
 * @function generateClassList
 * @static
 * @returns {array}
 */
const generateClassList = (context) => (['organization']);

/**
 * @function generateEntities
 * @param {object} repo Repostory for generating entities under a resource.
 * @returns {array}
 */
const generateEntities = (repo) => {
  return repo.getAll()
    .then((documentSnapshots) => {
      return documentSnapshots.map((doc) => {
        if (doc.exists) {
          let data = doc.data()
          return {
            //class: generateClassList(data.id),
            //rel: generateRelations(data.id),
            properties: data
            //links: generateLinksForItem(data.id, 'item', 'user')
          };
        }
      });
  });
};

/**
 * @function generateActions
 * @static
 * @returns {array}
 * @description
 * Ok, so here we're basically scripting with CasperJS. The presumption is 
 * that there will be a generic client through which we can automate browser 
 * behaviors and platform capabilities (Cordova). We can think of hypermedia 
 * controls as projections from rhetorical relations over a domain knowledge 
 * graph that represents the plurality of connections or links in hypermedia 
 * ensembles (differentiations of numerical and other kinds of identity).
 */
const generateActions = (_itemForms, entity, entityName) => {
    let host = `http://localhost:4000/api/${entityName}`;
  _itemForms.forEach(function (itemRef, key) {
    Object.keys(itemRef).forEach(function (keyRef) {
      if (itemRef[keyRef].includes('{id}')) {
        itemRef[keyRef] = itemRef[keyRef].replace(/{id}/, entity.id);
      }
      if (itemRef[keyRef].includes('{fullhost}')) {
        itemRef[keyRef] = itemRef[keyRef].replace(/{fullhost}/, host);
      }
      if (itemRef.hasOwnProperty('properties')) {
        itemRef.properties.forEach((p) => {
          Object.keys(p).forEach((k) => {
            if (p[k].includes('{status}')) {
              p[k] = p[k].replace(/{status}/, 'pending');
            }
          });
        });
      }
    });
  });
  return _itemForms;
};


module.exports = {
  cleanData,
  generateLinksForItem,
  generateLinksForList,
  generateClassList,
  generateActions,
  generateEntities
};

// EOF

这是我使用 generateLinksForList 函数的地方,因此我不能将 async 用于包含 map

/**
 * @module app/place/get
 * @description
 * Get all places.
 */
const { itemForms } = require('../../domain/place/transitions');
const { Place } = require('../../domain/place');
const {
  generateActions,
  generateLinksForList,
} = require('../../domain/helper.js');


module.exports = ({ placeRepository }) => {
  const all = () => {
    return Promise
      .resolve()
      .then(() => placeRepository.getAll()
        .then((documentSnapshots) => {
          return documentSnapshots.map((doc) => {
            let properties = doc.data() || {};
            let place = Place(properties);
            let links = generateLinksForList(place, 'item', 'place', placeRepository);
            let actions = generateActions(itemForms, doc, 'places');
            let props = Object.assign({}, place, {});

            let hypermediaResponse = {
              actions: actions,
              links: links,
              properties: props,
              class: ['place'],
              entities: []
            };

            if (doc.exists) {
              return hypermediaResponse;
            }
          });
        })
      )
      .catch(error => {
        throw new Error(error);
      })
  }

  return {
    all
  };
};

// EOF

回购功能:

/**
 * @module infrastructure/repositories/place/index
 * @description
 * Repo/methods for places.
 */
const { toEntity } = require('./transform');


module.exports = ({ model, database }) => {

  /**
   * @name getAll
   * @param {Object} args - Firestore clause (where, etc.); 
   * @returns {Object}
   */
  const _getAll = async (...args) => 
    await model
      .listDocuments().then((docRefs) => database.firestore.getAll(...docRefs));
  
  /**
   * @name getAll
   * @returns {Promise}
   */
  const getAll = async () =>
    await model.listDocuments()
      .then((docRefs) => database.firestore.getAll(...docRefs));

  /**
   * @name prev
   * @returns {undefined}
   */
  const prev = async (...args) => {
    let payload = args[0];
    let docRef = model.doc(payload.id);
    let snapshot = await docRef.get();
    let last = snapshot.docs[snapshot.docs.length - 1];
    return last.data();
  };

  /**
   * @name next
   * @returns {undefined}
   */
  const nextItem = async (...args) => {
    let payload = args[0];
    const docRef = model.doc(payload.id);
    const snapshot = (await docRef.get()).data();
    const start = await model
      .orderBy('createdAt')
      .endBefore(snapshot.createdAt)
      .limitToLast(1)
      //.startAt(snapshot)
      //.offset(1)
      //.limit(1)
      .get();
    let list = [];
    start.forEach((d) => {
      list.push(d.data());
    });
    return list;

  };

  /**
   * @name create
   * @returns {object}
   */
  const create = async (...args) => {
    let payload = args[0];
    let docRef = await model.doc(payload.id);
    await docRef.set(payload);
    return docRef.get().then((documentSnapshot) => {
      return documentSnapshot.data();
    })
  };

  /**
   * @name update
   * @returns {object}
   */
  const update = async (...args) => 
    await model.doc(args.id).update(...args);

  /**
   * @name findById
   * @returns {object}
   */
  const findById = async (...args) => 
    await model.where('id', '==', args.id).get();

  /**
   * @name findOne
   * @returns {object}
   */
  const findOne = async (...args) => await model
    .limit(1)
    .get();

  /**
   * @name destroy
   * @returns {object}
   */
  const destroy = async (...args) => 
    await model.doc(args.id).delete();

  return {
    getAll,
    create,
    update,
    findById,
    findOne,
    destroy,
    nextItem,
    prev
  };
};

// EOF

1 个答案:

答案 0 :(得分:1)

.forEach 不会等待异步函数。使用普通循环:

for ( const linkRelation of relations ) {
    for ( const key in linkRelation) {
        ...
        const l = await getL(repo, entityContext);
        ...
    }
}