Асинхронная функция: сообщать о ходе выполнения или событиях жизненного цикла?

Мне нравится использовать функции, возвращающие async-обещания, например:

// async style
await copyDirectory(`~/myfiles`,`~/copy-of-myfiles`)

// or even old-skool
copyDirectory(`~/myfiles`,`~/copy-of-my-files`)
.then(console.log)
.catch(console.error);

Однако иногда при выполнении асинхронных операций я хочу отслеживать прогресс (или вводить некоторые крючки жизненного цикла), например:

  • использовать beforeFileCopy крючок для логирования имени скопированного файла
  • использовать afterFileCopy подключите к логу имя скопированного файла (или сделайте что-нибудь более осмысленное)

Каков наиболее идиоматичный (элегантный / читаемый / принятый сообществом) способ выполнения такой операции. Я пробовал что-то вроде этого:

// example-1.js 
// This works and does the job, but feels very clumsy. 
await copyDirectory({
  fromDir: `~/myfiles`,
  toDir: `~/copy-of-my-files`, 
  beforeFileCopy: ev=>{
     console.log(`Copying file ${ev.filename}`);
  },
  afterFileCopy: ev=>{
     console.log(`Finished file ${ev.filename}`);
  } 
})

// example-2.js 
// Even if I use named functions for lifecycle hooks,
// it still looks clumsy to me (as those functions kind of look out out of place

await copyDirectory({
  fromDir: `~/myfiles`,
  toDir: `~/copy-of-my-files`,
  beforeFileCopy,
  afterFileCopy, 
})

function beforeFileCopy(ev){
  console.log(`Copying file ${ev.filename}`);
}

function afterFileCopy(ev){
  console.log(`Finished file ${ev.filename}`);
} 

Я также пытался сделать это с помощью шаблона {Event Emitter} (в основном избавляясь от Promises и просто используя эмиттер событий), и хотя он выглядит субъективно более «идиоматичным», без обещаний неудобно использовать его с async / await ( поскольку большая часть кода, с которым я работаю каждый день, является асинхронной функцией)

// This code follows EventEmitter pattern, but 
// now it is not following async/await paradigm..
copyDirectory({
  fromDir: `~/myfiles`,
  toDir: `~/copy-of-my-files`,
})
.on('before-file-copy', ev=>console.log(`Copying file ${ev.filename}`))
.on('after-file-copy', ev=>console.log(`Finished file ${ev.filename}`))
.on('finished', ()=>console.log(`Finished copying directory`))
.on('error', console.error);

Конечно, я могу попытаться обернуть шаблон EventEmitter в функцию, возвращающую обещание.

// Event Emitter wrapped into Promise-function

function copyDirectoryAsync(opts){
  return new Promise((resolve,reject)=>{

        copyDirectory({
          fromDir: `~/myfiles`,
          toDir: `~/copy-of-my-files`,
        })
        .on('before-file-copy', ev=>console.log(`Copying file ${ev.filename}`))
        .on('after-file-copy', ev=>console.log(`Finished file ${ev.filename}`))
        .on('finished', resolve)
        .on('error', reject);

  });

}

// Whilst I did manage to wrap EventEmitter into Promise-paradigm
// I am back to square one, where If I want to expose any lifecycle 
// hooks, I have to add them as parameters
await copyDirectoryAsync({
  fromDir: `~/myfiles`,
  toDir: `~/copy-of-my-files`,
})

// so if I want to expose lifecycle hooks from the EventEmitter 
// i need to expose functions beforeXX afterXX as options
// and I get to square one where I started 

await copyDirectoryAsync({
  fromDir: `~/myfiles`,
  toDir: `~/copy-of-my-files`,
  beforeFileCopy: ()=> .... ,
  afterFileCopy: ()=> .... , 
})

// and reminder that this is where I started 
await copyDirectory({
  fromDir: `~/myfiles`,
  toDir: `~/copy-of-my-files`,
  beforeFileCopy,
  afterFileCopy, 
})

Итак, что было бы идиоматическим (элегантным / рекомендованным сообществом / чистым) способом написания кода, который обеспечивает прогресс / жизненный цикл асинхронной операции?

1 ответ
1

Создавайте интерфейсы так, чтобы их было легко использовать, а не просто реализовать.

Если вам проще использовать вариант async, то нет необходимости реализовывать его с эмиттером событий и оборачивать его с помощью функции async.

Если проще использовать события, реализуйте это.

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

В любом случае, думали ли вы, что требуемые аргументы фактически разделяют параметры функции?

async function copyDirectory(source, target, hooks = {})

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *