LINUX.ORG.RU

Асинхронность и генераторы.

 


0

1

Сейчас везде пишут, что генераторы помогают писать асинхронный код. Я не понимаю, как они могут сделать это.

Возьмем для примера, простенькую функцию, которая асинхронно добавляет единицу в массив, и рассмотрим 2 версии, с генераторами и без них. Для простоты, ошибки ловить тоже не будем.




add = function(array){
  return new Promise(function(resolve){
   setTimeout(function(){
      array.push(1)
      resolve(array)
    })
  })
}


//////////////////  Реализация без генераторов /////////////////////
 
add([])
 .then(add)
 .then(add)
 .then(add)
 .then(add)
 .then(function(x) {console.log(x)})



///////////////// Реализация с генераторами ////////////////////////

helper = function(generator, yieldValue){
   var next = generator.next(yieldValue)
   
   if(!next.done) return next.value.then(function(result){
      return helper(generator, result)
   })
   console.log(next.value)
}

helper(function*(){

   var one = yield add([]),
       two = yield add(one),
       three = yield add(two),
       four = yield add(three),
       five = yield add(four)
       
   return five 
   
}())


Как видим, мы только наговняли лишнего кода, без какого бы то ни было выигрыша, к тому же, промисы все равно нужны в обоих случаях.

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



Последнее исправление: newquestion (всего исправлений: 1)

Насколько я понял - генераторы несколько параллельны промисам в данном вопросе.
Т.е. в одном случае ты должен отовсюду возвращать Promise и писать цепочки .then.then.then.catch.
В другом - везде возвращать генераторы и делать yield; yield; yield.
Какой-то принципиальной разницы я не вижу, разве что промисы вошли в стандарт, а для генераторов нужны велосипеды с квадратными колесами типа https://github.com/tj/co .
А скоро придет ES7 c async/await (ну либо можно начать обмазываться полифиллами уже сейчас) и эти велосипеды вымрут.

unikoid ★★★
()

Получить 2 значения внутрь одной функции без костылей и использовать их тут же? Может, бесполезно когда у тебя строго цепочка и тебе никогда не нужно one и three одновременно. Плюс мне нравится try/catch с ними.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Получить 2 значения внутрь одной функции без костылей и использовать их тут же?

Ну да, об этом я что-то не подумал. Для этого они и нужны, по-ходу. Спасибо.

newquestion
() автор топика
Ответ на: комментарий от x3al

Получить 2 значения внутрь одной функции без костылей и использовать их тут же?

Promise.all или я что-то не так понял?

unikoid ★★★
()
Ответ на: комментарий от unikoid

Получаешь two из one, потом three из two, потом юзаешь one и three вместе. Будет каша из коллбэков. .all() — немного не то, с ним *можно* накостылить, но с генераторами проще.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Получаешь two из one, потом three из two, потом юзаешь one и three вместе. Будет каша из коллбэков.

Да не такая уж и лапша

fetch = function(value){
  return new Promise(function(resolve){
   setTimeout(function(){
      resolve(value + 10)
    })
  })
}

sum = function(value1, value2){
   return new Promise(function(resolve){
     setTimeout(function(){
       resolve(value1 + value2)
     })
  })
}


promises = [fetch(1)]

promises[0]
 .then(n => promises[1] = fetch(n))
 .then(n => promises[2] = fetch(n))
 .then(()=> Promise.all(promises))
 .then(arr => sum(arr[0], arr[2]))
 .then(result => console.log(result)) // 42

Еще можно поспорить, где больше лапши.

А чем тебя отлов ошибок в промисах не устраивает?

newquestion
() автор топика
Ответ на: комментарий от newquestion

А чем тебя отлов ошибок в промисах не устраивает?

Тем, что забудешь catch — молча проигнорируется, исключение в catch — аналогично.

x3al ★★★★★
()
Ответ на: комментарий от x3al

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

newquestion
() автор топика
Ответ на: комментарий от x3al

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

newquestion
() автор топика
Ответ на: комментарий от newquestion

https://www.getsentry.com/welcome/ но можно и без этого. Любые необработанные выброшенные исключения можно ловить. А необработанные фейлы promise'ов в асинхронщине — нет, исчезают в никуда и дебажить их сложнее.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Но как ты их собрался ловить, если ты говоришь, что забываешь ставить catch? Кто их тогда ловить будет? Пушкин?

newquestion
() автор топика
Ответ на: комментарий от newquestion

Можно обернуть весь неасинхронный код в try/catch и ловить всё. Но так не поймаешь то, что в цепочках promise и вообще всю асинхронщину. А с генераторами — поймаешь поскольку даже дефолтный bluebird поднимает исключение.

x3al ★★★★★
()
Ответ на: комментарий от x3al

выброшенные исключения

И ты не путай эти «ошибки» с выброшенными исключениями. Выброшенные исключения ловит только родной try{}catch(e){}. А в реджект попадают просто строки, фактически, это юзер-левел *ошибки*, они к исключениям не имеют никакого отношения. А если ты исключение не поймал, оно наверх выскочит и программу завалит, такое не заметить — идиотом надо быть, поэтому непонятно вообще о каких трудностях дебага ты говоришь.

newquestion
() автор топика
Ответ на: комментарий от newquestion
a = new Promise(function(resolve, reject) {resolve(1);}).then(function(succ) { throw("error");});

Ошибка молча проглатывается, поскольку коллбэк в then выполняется асинхронно.

x3al ★★★★★
()
Ответ на: комментарий от x3al

да, тут надо catch ставить. Не знаю почему так сделали, дело тут не в асинхронности. Вот так

new Promise(function(resolve, reject){
   setTimeout(function(){throw "err"; resolve(1)})
})
 .then(function(success){})

все падает как обычно, хотя вызов асинхронный.

newquestion
() автор топика
Ответ на: комментарий от newquestion

все падает как обычно

Нет. Ты по-прежнему не можешь обернуть ни setTimeout, ни что-то вокруг него в try/catch.

x3al ★★★★★
()
Ответ на: комментарий от x3al

setTimeot в try/catch обернуть можно, но это глупо, поскольку асинхронный код выполниться вне этого блока.

newquestion
() автор топика
Ответ на: комментарий от x3al

Кстати, я до конца так и не понял, что там происходит в твоем коде, похоже, твой коллбек просто не отрабатывает. если в колбеке асинхронный код, все работает нормально

catcher = function(success){setTimeout(function(x){throw "err"})}

new Promise(function(resolve, reject){
     resolve(1)
})
 .then(catcher)

newquestion
() автор топика
Ответ на: комментарий от newquestion

Это исключение не ловится вообще ничем же. А хотелось бы. С генераторами — запросто, поскольку все запускатели этих генераторов бросают исключениями.

x3al ★★★★★
()
Ответ на: комментарий от x3al

почему не ловяться?

new Promise(function(resolve, reject){
     setTimeout(function(){try{resolve(undef)}catch(e){reject(e)}})
})
 .then(function(data){console.log(data)})
 .catch(function(err){console.log(err)})
//[ReferenceError: undef is not defined]
 

newquestion
() автор топика
Ответ на: комментарий от x3al

А можно и в resolve эту ошибку отправить, чтобы не прерывать выполнение. Это гибко довольно сделано, годно. Только вот с синхронным кодом действительно косяк там какой-то. Я так и не въехал, что там происходит на самом деле. Вроде бы, по идее, этот колбэк их then должет кидаться асинхронно, после события промиса, почему там throw не работает, хз.

newquestion
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.