2

I am newbie to promises, I am trying to understand how do they work.

Here my questions at first:

  1. When request is handled in a route function, does it wait for all promises, I mean that when I am using promise or callback it is a new scope and execution continues further.

  2. If I keep a req/res objects for example in timer and then respond to user, what will user see ? A request will just be blocked until I explicitly send response ?

So I have encountered the following problems.

Here is my route.

router.post('book', authHandler.provideMiddleware(), (req, res) => {
      bookManager.createBook(req.body, {
            onSuccess: function (data) {
               respondSuccess(res,HttpStatus.OK, {'data': data});
            },
            onError: function (message) {
                respondError(res, HttpStatus.HTTP_STATUS.BAD_REQUEST, {'error': message});
            }
        });
});

Inside bookmanager I have the following

  createBook(data, hook) {
        let book = createBookFromRequest(data);
        let verifyData = new Promise((resolve, reject) => {
            let valid = checkBookData(book);
            if(valid) {
               resolve(book);
            }
            else {
                reject("Invalid data");
            }
        });
        let createBook = new Promise((resolve, reject) => {
            book.save((err, result) => {
                if (!err) {
                    reject("Error while saving");
                }
                else {
                    resolve(result);
                }
            });

        });
        verifyData
            .then(() => {
                return createBook;
            })
            .then((data) => {
                hook.onSuccess(data);
            })
            .catch((error) => {
                hook.onError(error);
            });
    }

My idea is to chain multiple functions and if any error occurred, call hook.onError method, otherwise call on success.

I have several problems here.

  1. When error is thrown, my book is still created.
  2. I have the following error

    node:8753) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 6): Error: Can't set headers after they are sent.

I want to use the approach like Rx (Reactive Extensions).

Could someone explain what is wrong and how do promises really work in this case ?

1

1 Answer 1

3

1. When request is handled in a route function, does it wait for all promises, I mean that when I am using promise or callback it is a new scope and execution continues further.

It waits for you to send a response via res. You don't have to do that in response to the event, it's absolutely normal for it to be later, after an asynchronous process (like a promise resolution) completes.

2. If I keep a req/res objects for example in timer and then respond to user, what will user see ? A request will just be blocked until I explicitly send response ?

Yes.

I have several problems here.

1. When error is thrown, my book is still created.

You're always starting the process of creating the book, regardless of whether the data was verified as correct. new Promise starts the work.

2. I have the following error

node:8753) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 6): Error: Can't set headers after they are sent.

You're creating a promise and storing it in createBook, and never handling rejections of that promise if verifyData rejects. So you get an unhandled promise rejection.

You can get rid of the entire new Promise around saving the book, and just put it in the verifyData chain; see comments:

createBook(data, hook) {
    let book = createBookFromRequest(data);
    let verifyData = new Promise((resolve, reject) => {
        let valid = checkBookData(book);
        if (valid) {
            resolve(book);
        }
        else {
            reject("Invalid data");
        }
    });
    verifyData
        .then(() => book.save())  // The chain takes on the state of the promise
                                  // returned by `book.save`
        .then(data => {
            hook.onSuccess(data);
        })
        .catch((error) => {
            hook.onError(error);
        });
}

In that, I'm assuming createBookFromRequest and checkBookData are both synchronous processes, as that's how you're using them.

And actually, given that's the case, I don't see any need for the promise you're creating to verify the data. So it could be simpler:

createBook(data, hook) {
    let book = createBookFromRequest(data);
    if (checkBookData(book)) {
        book.save()
            .then(_ => hook.onSuccess(data))
            .catch(error => hook.onError(error));
    } else {
        hook.onError("Invalid data");
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Good answer! Just want to chip in with a comment that the verifyData.then(() => book.save()) can be rewritten as verifyData.then(book.save).then(...), though I'm quite sure you intentionally omitted that to make the example more clear! But, could always be useful to know for the OP! :)
@DanielB: Probably not: .then(book.save) will call book.save with this set to undefined during the call. But presumably it needs this set to book. The other option is .then(book.save.bind(book)) but I find the arrow function clearer. (bind is useful for if you want to avoid creating the closure, though.)
Doh, of course! I read the code too quick there, never realising it wasn't a save function taking the resolved book as an argument! Thanks T.J!
@DanielB: Sorry, forgot to say "thanks" above for the compliment on the answer! (I focussed too much on the then thing. :-) )

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.