【问题标题】:Promise run then() but don't return the right data承诺运行 then() 但不返回正确的数据
【发布时间】:2020-01-05 13:43:19
【问题描述】:

已解决,谢谢大家

编辑: 我希望我的 updateInventory 函数返回一个对象,该对象具有每个承诺的属性。 我既不知道将使用哪些,也不知道顺序 问题是t.batch 和Promise.all 返回一个索引数组,所以我不能将一个promise 关联到它的组件以输出我想要的对象。

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

首先执行getStatement 中的then 以格式化每个promise 的结果数据:具有name 和data 属性的对象。 然后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 };
  });
}

【问题讨论】:

  • 用行号来说话是非常困难的。请提供一个最小的示例以及什么不起作用。
  • @Vishnudev,最后的评论没有意义。调用 then 不会让代码等待。此外,它不会影响statement 的值。
  • 从t[repo] 开始的一些代码看起来有问题,这会导致调用上下文丢失。应该只传入t,然后在里面做t.repo。然后尝试两次解决承诺 - 一次在事务函数中,在调用 t.batch 时,另一次在 getStatement 中。
  • 如果statement 在你的getStatement 中是Promise(似乎是这种情况),那么输出(承诺数组)是正确的

标签: node.js promise


【解决方案1】:

一个问题是.then() 返回一个新的承诺,所以你需要改变这个:

  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 作为解析值的承诺。

【讨论】:

    【解决方案2】:

    您的期望是错误的,而您得到的输出是正确的(承诺数组)

    简单示例:

    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 中运行此 sn-p 时,我只能看到 {},而不是 Promise { &lt;state&gt;: "pending" },如果是这种情况,请打开浏览器以确认输出为 Promise

    解决方案?

    也许?:

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

    【讨论】:

      【解决方案3】:

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

      您可以在此示例中看到 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();

      但在您的情况下,它可能也不是一个选项,因为在那之后您将使用nam 和data 解决的承诺放入t.batch(statements);,这可能会失败。

      您需要以不同的方式处理此问题 - 即 getStatement 将返回数据结构,其中包含 DB 中的这些更新以及您需要的数据,然后您等待数据更新,然后再继续。

      【讨论】:

      • 注意他不能在他的上下文中使用Promise.all,因为他需要解决所有的承诺,这就是t.batch在那里所做的。
      猜你喜欢
      • 1970-01-01
      • 2016-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多