2

I'm creating a Node.js module to interact with my API, and I use the superagent module to do the requests. How it works:

module.exports = data => {
  return getUploadUrl()
    .then(uploadFiles)
    .then(initializeSwam)

  function getUploadUrl() {
    const request = superagent.get(....)
    return request
  }

  function uploadFiles(responseFromGetUploadUrl) {
    const request = superagent.post(responseFromGetUploadUrl.body.url)
    // attach files that are in data.files
    return request
  }

  function initializeSwam(responseFromUploadFilesRequest) {
    // Same thing here. I need access data and repsonseFromUploadFilesRequest.body
  }
}

I feel like I'm doing something wrong like that, but I can't think in a better way to achieve the same result.

FXux
  • 415
  • 6
  • 15
  • This will give you some ideas: [How to chain and share prior results with promises](https://stackoverflow.com/questions/28714298/how-to-chain-and-share-prior-results-with-promises/28714863#28714863). – jfriend00 Jan 04 '18 at 02:41

2 Answers2

6

Two simple ways:

  1. write your function to take all parameters it needs

    const doStuff = data =>
      getUploadUrl()
      .then(uploadFiles)
      .then(initializeSwam)
    

    might become

    const doStuff = data =>
      getUploadUrl()
        .then(parseResponseUrl) // (response) => response.body.url
        .then(url => uploadFiles(data, url))
        .then(parseResponseUrl) // same function as above
        .then(url => initializeSwam(data, url))
    

That should work just fine (or fine-ish, depending on what hand-waving you're doing in those functions).

  1. partially apply your functions

    const uploadFiles = (data) => (url) => {
      return doOtherStuff(url, data);
    };
    
    // same deal with the other
    const doStuff = data =>
      getUploadUrl()
        .then(parseResponseUrl)
        .then(uploadFiles(data)) // returns (url) => { ... }
        .then(parseResponseUrl)
        .then(initializeSwam(data));
    

A mix of all of these techniques (when and where sensible) should be more than sufficient to solve a lot of your needs.

Norguard
  • 26,167
  • 5
  • 41
  • 49
1

The way you have your code structured in the above snippet results in the getUploadUrl(), uploadFiles(), and initializeSwam() functions not being declared until the final .then(initializeSwam) call is made. What you have in this final .then() block is three function declarations, which simply register the functions in the namespace in which they are declared. A declaration doesn't fire-off a function.

I believe what you want is something like:

async function getUploadUrl() {             <-- notice the flow control for Promises
  const request = await superagent.get(....);
  return request;
}

async function uploadFiles(responseFromGetUploadUrl) {
  const request = await superagent.post(responseFromGetUploadUrl.body.url)
  // attach files that are in data.files
  return request;
}

async function initializeSwam(responseFromUploadFilesRequest) {
  // Same thing here. I need access data and 
  repsonseFromUploadFilesRequest.body
  const request = await ...;
}

module.exports = data => {
  return getUploadUrl(data)      <-- I'm guessing you wanted to pass this here
    .then(uploadFiles)
    .then(initializeSwam);
}

This approach uses ES6 (or ES2015)'s async/await feature; you can alternatively achieve the same flow control using the bluebird Promise library's coroutines paired with generator functions.

dsuare1
  • 11
  • 3