承诺然后运行then(),但不会返回正确的数据

时间:2019-09-02 14:03:30

标签: node.js promise

已解决问题,谢谢大家

编辑: 我希望我的updateInventory函数返回一个具有每个promise属性的对象。 我既不知道将使用哪个命令,也不知道命令的顺序 问题是t.batchPromise.all返回一个索引数组,因此我无法将Promise与它的组件关联以输出所需的对象。

受这个post的启发,我这样做了:

首先执行then中的getStatement来格式化每个Promise的结果数据:具有namedata属性的对象。 然后使用所有这些对象解析t.batch。结果仍然是索引数组,但是每个对象都包含名称和数据。 这样我就可以创建我的最终对象。 也许有更好的方法吗?

以下示例: 我的输入:

{
    device: {
        deviceId: 1,
        name: "myName",
        istate: "updated"
    },
    system: {
        systemId: 50,
        domain: 'foo.fr',
        istate: "updated"
    },
    ifaces: [
        {
            interfaceId: 75,
            name: "Iface Update 1",
            istate: "updated"
        },
        {
            name: "New Interface 1",
            istate: "added"
        }
    ]
}

仅带有t.batch()或`Promise.all()的“标准”输出:一个索引数组

[
    0: { deviceId: 1 },
    1: { systemId: 50 },
    2: [
        { interfaceId: 75 },
        { interfaceId: 76 }
    ]
]

我的自定义t.batch输出:

[
    0: { name: "device", data: { deviceId: 1 }}
    1: { name: "system", data: { systemId: 50 }}
    2: { name: "ifaces", data: [{ interfaceId: 75 },{ interfaceId: 76 }] }
]

然后我可以构建我的最终对象(下面的values.forEach):

{
  "device": { deviceId: 1 },
  "system": { systemId: 50 },
  ifaces: [
        { interfaceId: 75 },
        { interfaceId: 76 }
  ]
}

代码(简体):

function updateInventory(params) {
  const { /* hidden */, ...components } = params.data; // simplified

  return Database.tx('Inventory-Update', t => { // postgres transaction with pg-promise library
    const statements = []; // promise array

    for(let [prop, component] of Object.entries(components)) {
      statements.push(getStatement(t, prop, component));
    }

    return t.batch(statements);
  }).then(results => {
    let result = {};
    results.forEach(res => {
      result[res.name] = res.data;
    });
    return result;
  });
}

function getStatement(t, prop, component) {
  const repo = getReposName(prop);
  const state = component.istate;

  let statement;

  switch(state) {
    case "updated":
      statement = t[repo].update(component);
      break;
    case "removed":
      statement = t[repo].delete(component);
      break;
    default:
      statement = t[repo].insert(component);
      break;
  }

  return statement.then(res => {
    return {name: prop, data: res };
  });
}

3 个答案:

答案 0 :(得分:1)

它基于Promises的工作方式。 then表示承诺得到解决后,它将运行下一部分。但是,您可以将多个then添加到单个承诺中,并且它们不会互相影响。

在此示例中,您可以看到Promise all在then语句之前已解决(因为它只是等待语句完成,而不是waitABit部分)。

function getStatement() {
  const statement = waitABit();

  statement.then(res => {
    let foo={name: Math.random(), data: [Math.random()] };
    console.log(foo);
    return foo;
  });
  return statement;
}

async function waitABit() {
  return new Promise(resolve => setTimeout(resolve,1000));
}

async function callAll() {
  statements = [];
  for (let i=0; i < 3; i++){
    statements.push(getStatement());
  }
  console.log(await Promise.all(statements));
}

callAll();

此部分易于修复:

    function getStatement() {
      let statement = waitABit();

      const afterStatement = statement.then(res => {
        let foo={name: Math.random(), data: [Math.random()] };
        console.log(foo);
        return foo;
      });
      return afterStatement;
    }

    async function waitABit() {
      return new Promise(resolve => setTimeout(resolve,1000));
    }

    async function callAll() {
      statements = [];
      for (let i=0; i < 3; i++){
        statements.push(getStatement());
      }
      console.log(await Promise.all(statements));
    }

    callAll();

但是在您的情况下,它也可能不是一个选择,因为在那之后,您将把将用namdata来解决的承诺放到t.batch(statements);中,这很可能会失败。 / p>

您需要以不同的方式进行处理-即getStatement将返回数据库中包含这些更新以及所需数据的数据结构,然后等到数据更新后再继续操作。

答案 1 :(得分:1)

您的期望是错误并且您获得的输出是正确的(承诺数组)

简单的例子:

function getPromise() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve("data"), 200)
  })
}

function something() {
  // codes ...
  let res = getPromise()
  res.then(d => {
    // you're expecting this to be returned, but remember this runs in Future
    // this part of execution happens after res is returned in the last line
    console.log('this is executed after return')
    return d;
  })
  return res; // <-- this is what is actually returned
}

console.log(something())

注意:由于某种原因,在Firefox中运行此代码段时,我只能看到{},而看不到Promise { <state>: "pending" },如果是这种情况,请打开浏览器以确认输出为Promise

解决方案?

也许吗?:

// get rid of statement.then...
return { name, data: statement };

答案 2 :(得分:1)

一个问题是 statement.then(res => { let foo={name: name, data: res }; console.log(foo); return foo; }); return statement; 返回了一个新的承诺,因此您需要更改它:

  return statement.then(res => {
    let foo={name: name, data: res };
    console.log(foo);
    return foo;
  });

对此:

.then()

您需要返回foo返回的新承诺,因为这是将{{1}}作为已解决值的承诺。