Выполнить дерево обещаний последовательно от родителя к ребенку - PullRequest
0 голосов
/ 08 января 2019

Я хочу создать древовидную структуру, используя вложенные Promise.

Когда обещания разрешаются в древовидной структуре, они разрешаются изнутри (потомок, потом родитель). Мне нужно, чтобы выполнение обещаний от родителя к брату и сестре могло выполняться параллельно.

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

Я бы предпочел не использовать async / await и просто придерживаться Promise или другого объекта Functional JS.

В этом первом примере будет показан разрешенный порядок вложенных Обещаний.

let order = 0
const promiseTree = (name, children) => 
  Promise.all([
    new Promise(res => res(`${name} order:${order++}`)),
    children && Promise.all(children)
  ])

promiseTree('root', [
  promiseTree('child', [
    promiseTree('grandchild', [
      promiseTree('great grandchild sibling 1'),
      promiseTree('great grandchild sibling 2'),
    ])
  ])
])
.then(console.log)
<script src="https://codepen.io/synthet1c/pen/KyQQmL.js?concise=true"></script>

Если вы решите закрытие, то рекурсивно вызывайте обратные вызовы, как только все обещания будут выполнены, порядок можно исправить.

let order = 0
const promiseTree = (name, children) => 
  Promise.all([
    // --------------------- resolve a closure with the resolved value contained
    new Promise(res => res(() => `${name} order:${order++}`)),
    children && Promise.all(children)
  ])

// flatMap over the tree, if it's a function call it and return the result
const recursivelyCall = x => 
  Array.isArray(x)
    ? x.map(recursivelyCall)
    : typeof(x) === 'function' ? x() : x

promiseTree('root', [
  promiseTree('child', [
    promiseTree('grandchild', [
      promiseTree('great grandchild sibling 1'),
      promiseTree('great grandchild sibling 2'),
    ])
  ])
])
// traverse the returned values and call the functions in declared order
.then(recursivelyCall)
.then(console.log)
<script src="https://codepen.io/synthet1c/pen/KyQQmL.js?concise=true"></script>

Приветствия

1 Ответ

0 голосов
/ 30 января 2019

Проблема с шаблоном в коде в первом фрагменте заключается в том, что самая внутренняя вложенная функция (аргумент) выполняется первой; см. и выделите строку 15 на https://astexplorer.net/#/gist/777805a289e129cd29706b54268cfcfc/5a2def5def7d8ee91c052d9733bc7a37c63a6f67, https://github.com/tc39/ecma262/issues/1397.

Я бы предпочел не использовать async / await и просто придерживаться Promise или другой функциональный объект JS.

Непонятно, почему этот вариант исключен из рассмотрения. Функция async является экземпляром Promise. Учитывая требование async/await является жизнеспособным решением.

console.log((async() => void 0)() instanceof Promise);

const fn = async() => {
  let order = 0
  const promiseTree = name =>
    new Promise(res => res(`${name} order:${order++}`))

  const res = [await promiseTree('root'), [
    await promiseTree('child'), [
      await promiseTree('grandchild'), [
        await promiseTree('great grandchild sibling 1')
      , await promiseTree('great grandchild sibling 2')
      ]
    ]
  ]];
  return res;
}

fn()
.then(console.log)
...