Wrong promise.all() usage between functions?

问题: I made this dummy code to have a better understanding on how promises work, mimicking a more complex software that I have to "promisify". In the attached code, I wanted the...

问题:

I made this dummy code to have a better understanding on how promises work, mimicking a more complex software that I have to "promisify". In the attached code, I wanted the events to fire and be logged in the following order:

  • the "before" string
  • the list of "... detected"
  • the list of "done with ..."
  • the "after" string

But as you can see if you run it, the "after" string is printed between steps 2 and 3. Clearly, I must be doing something wrong in handling async logic. Thanks for your help!

const obj = {
  "rows": [{
    "type": "A",
    "value": 0
  }, {
    "type": "B",
    "value": 0
  }, {
    "type": "C",
    "value": 0
  }]
}
let promises = [];

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

const alter_value = async(row, to_add, time) => {
  await delay(time);
  row.value = row.value + to_add;
  console.log("done with " + row.type);
  return true;
}

const two = async() => {
  obj.rows.forEach(async(row) => {
    switch (row.type) {
      case "A":
        console.log("A detected");
        promises.push(alter_value(row, 1, 1000))
        promises.push(alter_value(row, 2, 1800))
        break;
      case "B":
        console.log("B detected");
        promises.push(alter_value(row, 5, 1400))
        break;
      case "C":
        console.log("C detected");
        promises.push(alter_value(row, 200, 2400))
        break;
    }
  });

  return promises;
}

const one = async() => {
  console.log("before");
  Promise.all(two()).then(console.log("after"));
}

one();


回答1:

I see at least two issues with your code, explaining the result you're getting:

  1. Your two function should not be async. async functions return an implicit Promise. Here you just want to return an array of Promises that you already construct yourself, so a normal function is what you need.
  2. .then(console.log("after")) will execute the console.log right away: then() expects a function to execute later, so you have to change it to .then(() => console.log("after")).

This becomes:

const obj = {
  "rows": [{
    "type": "A",
    "value": 0
  }, {
    "type": "B",
    "value": 0
  }, {
    "type": "C",
    "value": 0
  }]
};

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

const alter_value = async (row, to_add, time) => {
  await delay(time);
  row.value = row.value + to_add;
  console.log("done with " + row.type);
  return true;
};

const two = () => {
  const promises = [];
  
  obj.rows.forEach(async(row) => {
    switch (row.type) {
      case "A":
        console.log("A detected");
        promises.push(alter_value(row, 1, 1000));
        promises.push(alter_value(row, 2, 1800));
        break;
      case "B":
        console.log("B detected");
        promises.push(alter_value(row, 5, 1400));
        break;
      case "C":
        console.log("C detected");
        promises.push(alter_value(row, 200, 2400));
        break;
    }
  });

  return promises;
};

const one = async () => {
  console.log('before');
  Promise.all(two()).then(() => console.log('after'));
};

one();

Note that, as an alternative to .then(), you can simply use await on Promise.all as well, to make your code more consistent:

await Promise.all(two());
console.log('after');

回答2:

It looks like the OP requests synchronous behavior.

Demo Outline

  • function modVal(i, value, time) parameters are derived from the async function syncro() parameter: const sync an array of objects. Each object contains the index of obj.rows, the value at that index obj.rows[i].value, and the time for the timeout in the Promise of modVal().

  • Parameters and Arguments: const sync = [{r: 0, v: 1, t: 1000}, ...];
    seq.r: obj.rows[number]
    seq.v: obj.rows[seq.r].value += number
    seq.t: ...resolve(obj.rows[i].value += value), number);

  • sync array is iterated by a for...of loop. On each iteration await modVal() is called synchronously.

let obj = {
  "rows": [{
    "type": "A",
    "value": 0
  }, {
    "type": "B",
    "value": 0
  }, {
    "type": "C",
    "value": 0
  }]
}

const sync = [{
  r: 0,
  v: 1,
  t: 1000
}, {
  r: 0,
  v: 2,
  t: 1800
}, {
  r: 1,
  v: 5,
  t: 1400
}, {
  r: 2,
  v: 200,
  t: 2400
}];

const syncro = async(sync) => {
  const modVal = (i, value, time) => {
    return new Promise(resolve => {
      setTimeout(() => resolve(obj.rows[i].value += value), time);
    });
  }

  for (let seq of sync) {
    await modVal(seq.r, seq.v, seq.t);
    console.log(JSON.stringify(obj.rows));
  }
}

syncro(sync);
.as-console-row.as-console-row::after {
  content:'';
  padding:0;
  margin:0;
  border:0;
  width:0;
}

  • 发表于 2019-03-31 07:50
  • 阅读 ( 164 )
  • 分类:sof

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除