hans_maulwurf
hans_maulwurf

Reputation: 23

Using callback functions inside sagas

Ok, so I've just spent a day figuring out how to use callback-functions within sagas. (Please be nice, I'm just learning this saga-stuff)

My initial problem: I get an xml-response from the server and before going into my reducer I want to parse it into a js-object. Therefore I use xml2js.

Calling this xml2js library works with a callback:

parseString(xmlInput, (err, jsResult) => {
  // here I'd like to put() my success-event, however that's not possible in this scope
})

After reading a lot about eventChannels, I've come up with this solution:

My Channel-Function:

function parseXMLAsyncronously (input) {
  return eventChannel(emitter => {
    parseString(input, (err, result) => {
      emitter(result)
      emitter(END)
    })
    return () => {
      emitter(END)
    }
  })
}

Using it inside the saga:

const parsedJSObject = yield call(parseXMLAsyncronously, xmlFromServer)
const result = yield take(parsedJSObject)

The problem that I'm encountering now is that apparently even while using a callback-structure, the parseString-function is still executed synchronously. So when I get to my yield-line, the parsing has already been done and I can wait forever because nothing will happen anymore.

What's working is to make the parsing asynchronously, by replacing

parseString(input, (err, result) => {...}

with

const parser = new Parser({async: true})
parser.parseString(input, (err, result) => {...}

So basically I'm making an already blocking function unblocking just to block (yield) it again and then wait for it to finish.

My question is now pretty simple: Is there maybe a smarter way?

Upvotes: 0

Views: 288

Answers (1)

Martin Kadlec
Martin Kadlec

Reputation: 4975

Why not just use the cps effect?

try {
  const result = yield cps(parseString, input)
} catch (err) {
  // deal with error
}

Upvotes: 1

Related Questions