Home > Back-end >  Lamda function invocation ends before resolving the promise
Lamda function invocation ends before resolving the promise

Time:09-16

I have the following code to read a file from a URL and then upload it to another destination:

import request from 'request';
import FormData from 'form-data';

export const handler = async (event, context) => {
  new Promise((resolve, reject) => {
    const form = new FormData();
    console.log('Streaming ...');
    form.append('file', request('https://cdn.mysite.com/video.mp4'));
    console.log('Uploading ...');
    // Invocation ends here!
    form.submit('https://www.someapi.com/upload', (error, response) => {
      if (error) reject(error);
      let body = '';
      response.on('data', chunk => {
        console.log('Receiving response ...');
        body  = chunk.toString()
      });
      response.on('end', () => {
        console.log('Done ...');
        resolve(JSON.parse(body))
      });
      response.resume();
    });
  });
};

Running this code locally works fine, but when I deploy and run it on AWS Lambda it ends before submitting the form. I tried to remove the Promise and run the code inside it but got the same result!

I don't know if it starts the submission or not, but the last thing I see on the cloud logs is Uploading ..., and the invocation ends after it immediately.

How to make the Lamda function waits until the promise is resolved?

CodePudding user response:

This is missing a return statement as mentioned by Mark in the comments:

import request from 'request';
import FormData from 'form-data';

export const handler = async (event, context) => {
  // --- here, promises work with return values
  return new Promise((resolve, reject) => {
   // ... rest of your code
  });
};

But can be simplified extensively (assuming modern Node.js) without any nesting:

import request from 'request';
import FormData from 'form-data';
import { promisify } from 'util';

export const handler = async (event, context) => {
    const form = new FormData();
    console.log('Streaming ...');
    form.append('file', request('https://cdn.mysite.com/video.mp4'));
    console.log('Uploading ...');
    // Invocation ends here!
    await promisify(cb => form.submit('https://www.someapi.com/upload', cb))()
    const bodyParts = await response.toArray();
    return response.map(x => x.toString()).join('');
};

In general you almost never need to wrap things in new Promise in code.

CodePudding user response:

I was able to resolve this by using a non-async handler, and removing the promise from the code. Here's a working copy of my code:

import request from 'request';
import FormData from 'form-data';

export const handler = (event, context, callback) => {
  const form = new FormData();
  console.log('Streaming ...');
  form.append('file', request('https://cdn.mysite.com/video.mp4'));
  console.log('Uploading ...');
  form.submit( 'https://www.someapi.com/upload', (error, response) => {
    if (error) throw error;
    let body = '';
    response.on('data', chunk => {
      body  = chunk.toString();
    });
    response.on('end', () => {
      console.log('Done ...');
      callback(null, JSON.parse(body));
    });
    response.resume();
  });
};

In this case, the Lambda function will wait for the callback to terminate, based on the AWS Lambda function handler in Node.js documentation.

CodePudding user response:

I believe this has something to do with the default timeout value of Lambda.

By default, Lambda stops executing a function after 3 seconds.

If your code needs to run longer, you can modify the timeout settings in General Configuration of a specific Lambda function:
[ ] https://docs.aws.amazon.com/lambda/latest/dg/configuration-function-common.html#configuration-common-summary

The maximum timeout value is 15 minutes as of 15-09-2022.

I recommend you try increasing this value.

Hope this helps!!

  • Related