Aayushi Kesharwani
Aayushi Kesharwani

Reputation: 11

Getting Promise { <pending> } while working with Nodejs in IBM cloud function

I read several answers related to it but was only getting confused.

So, my problem is : I have to read the content of my CSV file stored in IBM cloud object storage using an IBM cloud function.

For that I created a "Read action" using Nodejs as the runtime. I wrote the following code in it and it shows the 'Promise pending' error.

This is my code:

const COS = require('ibm-cos-sdk').S3

function main (params) {
  const IAM_API_KEY = process.env.__OW_IAM_NAMESPACE_API_KEY
  const ENDPOINT = 's3.eu-gb.cloud-object-storage.appdomain.cloud'
  const BUCKET = 'my-bucket'
  const FILE = 'Sample.csv'

  const config = {
    endpoint: ENDPOINT,
    apiKeyId: IAM_API_KEY
  }

  const cos = new COS(config)

  const options = {
    Bucket: BUCKET
  }

  function getItem (bucketName, itemName) {
    console.log(`Retrieving item from bucket: ${bucketName}, key:${itemName}`)

    return cos.getObject({
      Bucket: bucketName,
      Key: itemName
    }).promise()
      .then((data) => {
        if (data != null) {
          console.log('File Contents: ' + Buffer.from(data.Body).toString())
          console.log('hellllllo')
        }
      })
      .catch((e) => {
        console.error(`ERROR: ${e.code} - ${e.message}\n`)
      })
  }

  console.log(getItem(BUCKET, FILE))
}

Output:

Retrieving item from bucket: 
my-bucket, key: Sample.csv",

"2019-09-01T19:28:35.905526Z stdout: Promise {pending}"

Upvotes: 0

Views: 1317

Answers (2)

CherryDT
CherryDT

Reputation: 29052

Short answer

That's not an error, that's actually the value returned - a pending promise. Read this to learn what promises are. You see this because at the point at which you log the value, the value is not even there yet - the CSV data is still being fetched.

As immediate fix to your problem, you would need to change this...

console.log(getItem(BUCKET, FILE))

...to this:

return getItem(BUCKET, FILE).then((data) => {
  console.log(data)
})

This will return the promise back to the IBM cloud so that it can wait for the operation to complete, and it will execute your console.log statement only once the data was returned instead of doing that immediately.

Additionally, you will need to add this...

return data

...at the end of the callback inside your then call (below your if (data !== null) block) - otherwise you'd now see only undefined outside.

Long answer

Promise {pending} is not an error. It's the return value of the getItem function: a pending promise.

If you don't know what a promise is read about them here.

In a nutshell, a promise represents an operation that completes asynchronously. At the point as which you are console.log-ing the promise, it doesn't even have a value yet, as the CSV fetching operation is still in progress.

A short example to show the execution order of operations if promises come into play:

// Let's assume that the function delay(s) returns a promise that
// resolves only after the given amount of seconds has elapsed

console.log('A')
delay(1).then(function () {
  console.log('B')
  return delay(2)
}).then(function () {
  console.log('C')
})
console.log('D')

// Output will be:
// (immediately)
//   A
//   D <<<<< !!!!!!!
// (1 second later)
//   B
// (2 seconds later)
//   C

To illustrate what is happening, let me reduce the code to the part that happens until you see this log output:

  function getItem (bucketName, itemName) {
    console.log(`Retrieving item from bucket: ${bucketName}, key:${itemName}`)

    return somePromise
  }

  console.log(getItem(BUCKET, FILE))

Of course, somePromise doesn't exist as variable in your code, but I use it here to represent the return value of cos.getObject(...).promise(), which is a promise.

So, in the initial execution of your code, you see Retrieveing item from bucket and then the returned value, which is the pending promise. This matches what you described.

However, your code is not done yet at that point. The fetching operation has only been initiated, not completed. Once it has completed, the code inside your then or catch handler will run as well! So, I'd expect that a second later you'd also see File Contents:... and hellllllo, or ERROR:... in your output. (If you don't, then it's possible that your execution environment doesn't support asynchronous tasks or has a time limit for execution that is too short.)

If you do see your output later but you are not sure how to use the fetched value in further code: Once there is any asynchronous component, all code using that component must be asynchronous itself. This means, you cannot simply return the value in main for example, you'd have to return a promise from main as well, and the outer code that uses main would also have to know how to handle the promise, and so on. Luckily, the IBM cloud does support asynchronous reponses, so just make sure you return a promise from main as well, e.g. use return getItem(BUCKET, FILE).

Even nicer solution

The code can become much nicer to read and write if you use the async/await syntax that comes with Node v8. IBM cloud allows using node v8 if you select the right runtime. In this case, the asynchronous code would simplify to this:

const COS = require('ibm-cos-sdk').S3

async function main (params) { // <<< async keyword added
  const IAM_API_KEY = process.env.__OW_IAM_NAMESPACE_API_KEY
  const ENDPOINT = 's3.eu-gb.cloud-object-storage.appdomain.cloud'
  const BUCKET = 'my-bucket'
  const FILE = 'Sample.csv'

  const config = {
    endpoint: ENDPOINT,
    apiKeyId: IAM_API_KEY
  }

  const cos = new COS(config)

  const options = {
    Bucket: BUCKET
  }

  async function getItem (bucketName, itemName) { // <<< async keyword added
    console.log(`Retrieving item from bucket: ${bucketName}, key:${itemName}`)

    try {
        const data = await cos.getObject({ // <<< turned into assignment with await
          Bucket: bucketName,
          Key: itemName
        }).promise() // <<< no .then with callback needed

        if (data != null) {
          console.log('File Contents: ' + Buffer.from(data.Body).toString())
          console.log('hellllllo')
        }

        return data
      } catch (e) { // <<< turned into simple catch
        console.error(`ERROR: ${e.code} - ${e.message}\n`)
      }
  }

  console.log(await getItem(BUCKET, FILE)) // <<< await keyword added
}

This still uses promises under the hood, but the new syntax allows you to write the code in a way that makes it look more natural and easier to understand.

Upvotes: 1

Hemant Parashar
Hemant Parashar

Reputation: 3774

getItem will be returning a Promise. You can tag a .then in the end with a callback function that'll be called when the Promise gets resolved.

getItem(BUCKET, FILE).then(results => {
    console.log(results);
});

Hope this helps !

Upvotes: 0

Related Questions