Неправильное использование promise.all() между функциями?

#javascript #asynchronous #es6-promise

#javascript #асинхронный #es6-обещание

Вопрос:

Я создал этот фиктивный код, чтобы лучше понимать, как работают обещания, имитируя более сложное программное обеспечение, которое я должен «обещать». В прилагаемом коде я хотел, чтобы события запускались и регистрировались в следующем порядке:

  • строка «до»
  • список «… обнаружено»
  • список «сделано с …»
  • строка «после»

Но, как вы можете видеть, если вы запустите его, строка «после» печатается между шагами 2 и 3. Очевидно, я, должно быть, делаю что-то неправильно при обработке асинхронной логики. Спасибо за вашу помощь!

 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. const jsonArray = { ... } — Это не массив и не массив JSON. Это всего лишь обычный старый объект.

2. ты прав, @ Andreas, я исправил его название

Ответ №1:

Я вижу по крайней мере две проблемы с вашим кодом, объясняющие результат, который вы получаете:

  1. Ваша two функция не должна быть async . async функции возвращают неявное Promise значение. Здесь вы просто хотите вернуть массив Promise s, который вы уже создали самостоятельно, поэтому вам нужна обычная функция.
  2. .then(console.log("after")) console.log сразу же выполнит: then() ожидает, что функция будет выполнена позже, поэтому вам нужно изменить ее на .then(() => console.log("after")) .

Это становится:

 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();  

Обратите внимание, что в качестве альтернативы .then() вы также можете просто использовать await on Promise.all , чтобы сделать ваш код более согласованным:

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

Ответ №2:

Похоже, что OP запрашивает синхронное поведение.

Демонстрационный план

  • modVal(i, value, time) параметры функции являются производными от syncro() параметра функции async: const sync массива объектов. Каждый объект содержит индекс obj.rows , значение в этом индексе obj.rows[i].value и время ожидания в обещании modVal() .

  • Параметры и аргументы: const sync = [{r: 0, v: 1, t: 1000}, ...];
    seq.r: obj.rows[ число ]
    число obj.rows[seq.r].value = после t:
    число ...resolve(obj.rows[i].value = value), после v:);

  • sync массив повторяется for...of циклом. На каждой итерации await modVal() вызывается синхронно.

 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;
}