zzzgoo
zzzgoo

Reputation: 2235

Is there a way to end a thenable chain?

let textProcess = new Promise((resolve, reject) => {
    let text
    try {
        // fetch text from the internet
        text = "str"
        resolve(text)
    } catch (e) {
        reject("failed to fetch!")
    }
})

textProcess.then(data => {
    let json
    try {
        json = JSON.parse(data)
    } catch (e) {
        console.error("failed to parse!")
        // ..........i want to end the whole process here, never go ahead
    }
}, e => {
    // try to fetch text from local chache
}).then(json => {
    // work on the json obj
}, e => {
    // if failed to fetch from local too, just let it go
})

Is there a way to end a thenable chain?

Look at the example above, I want to end the whole process when parsing is failed(the line preceeded with ".........."). But actually the last then will still be invoked though.

What is the proper and elegant way to achieve my goal?

Upvotes: 1

Views: 102

Answers (2)

traktor
traktor

Reputation: 19301

The short answer is no, there is no mechanism to terminate a promise chain in a then handler part-way down the chain. (A proposal for promise cancellation was made to the TC39 committee in 2016 but was subsequently withdrawn.)

Note that a "promise chain" often refers to the promise returned by the last then, catch or finally call in a chain of single promise method calls concatenated together.

All calls to the promise methods listed above are made synchronously when code defining the chain is executed. After execution, all promises in the chain have been created and all method calls in the chain called.

Since promises only have three states (pending, fulfilled and rejected), the best you can do is to arrange for "cancellation" to be sent down the rejection channel of linked promises and ignore it as required. (There is no standard "cancelled" rejection value to use).

Upvotes: 0

woozyking
woozyking

Reputation: 5220

Your Promise usage involves quite a bit of sub-optimal patterns. Fixing them actually leads to what you're trying to achieve too.

textProcess.then(data => {
  // 1st anti-pattern fix
  // any error triggered here
  // also gets caught at the end catch
  return JSON.parse(data)
}).then(json => {
  // work on json obj
}).catch(e => {
  // 2nd anti-pattern fix
  // one catch for the whole thenable chain
  console.error("Failed to parse!", e)
})

This way, you properly leverage what Javascript Promise offers, and one simple .catch for what you need.


Edit - some explanations on involved Promise anti-patterns

The marked 1st anti-pattern is about unnecessary nested try..catch block within then. Within it, you can return synchronously (even undefined), another Promise (both of these are thenable), or throw an Error (which would get caught by catch). Basically you don't need to explicitly catch it but let it "flow" through.

The 2nd anti-pattern as mentioned is the fact that the second parameter -- reject handler of then is considered sub-optimal in most use cases. A Promise chain should be leveraging one catch to simplify the workflow.

However, in the rare event of the need to perform "early catch" and "resume", consider the following way, which is still a bit clearer than using two handlers for then:

textProcess.then(data => {
  return parser1(data)
}).catch(e => {
  console.log("First parser failed")
  // for example first parser failed
  return "fallback data"
}).then(data => {
  // process received data as "fallback data"
}).catch(e => {
  // always have a "last resort" catch at the end of the workflow
})

Upvotes: 1

Related Questions