Ali Yousuf
Ali Yousuf

Reputation: 702

Promise.then bind issue

I have been facing an issue in binding a value to next Promise. Look at the following code, it will explain the situation better.

'use strict';

function FindEvent(eventId) {
    console.log('Event:', eventId);
    return Promise.resolve({title: 'MyEvent'});
}

function FindUser(userId) {
    console.log('User:', userId);
    return Promise.resolve({username: 'MyUser'});
}

function MyServiceProblem(payload) {
    payload.userId = 'blah';
    return FindEvent(payload.eventId)
        .then((event) => {
            payload.userId = 'test';
            // setting a breakpoint here shows
            // that the value to payload.userId has been
            // assigned, i.e. 'test'

            return Promise.resolve(payload);
        })
        .then(FindUser.bind(this, payload.userId));
        // But new value doesn't reaches inside FindUser
}

MyServiceProblem({ // userId is 'blah', why not 'test'
    eventId: '456'
});

function MyServiceWorks(payload) {
    payload.userId = 'blah';
    return new Promise((resolve) => {
        payload.userId = 'test';

        return resolve(payload);
    })
        .then(FindUser.bind(this, payload.userId));
        // From here, the 'test' value reaches FindUser
}

MyServiceWorks({ // userId is 'test'
    eventId: '456'
});

The question is, why the value it binds is different for both cases. Both are exactly the same, except this one is first resolving the part of promise and then assigning a value to payload.userId.

Upvotes: 0

Views: 2114

Answers (1)

loganfsmyth
loganfsmyth

Reputation: 161557

Let's break down your code a little bit. You have

function MyServiceProblem(payload) {
    payload.userId = 'blah';
    return FindEvent(payload.eventId)
        .then((event) => {
            payload.userId = 'test';

            return Promise.resolve(payload);
        })
        .then(FindUser.bind(this, payload.userId));
}

the issue is that your .bind will run before your callback. This code could also be written as

function MyServiceProblem(payload) {
    payload.userId = 'blah';

    var firstThenCallback = (event) => {
        payload.userId = 'test';
        return Promise.resolve(payload);
    };
    var secondThenCallback = FindUser.bind(this, payload.userId);

    return FindEvent(payload.eventId)
        .then(firstThenCallback)
        .then(secondThenCallback);
}

The payload object is shared across everything, but the payload.userId is passed into .bind before firstThenCallback has had a chance to execute.

Rather that using .bind and passing the value immediately, it seems like your simplest solution would be to use an anonymous function so the userId value is only read later.

function MyServiceProblem(payload) {
    payload.userId = 'blah';
    return FindEvent(payload.eventId)
        .then((event) => {
            payload.userId = 'test';

            return Promise.resolve(payload);
        })
        .then(() => FindUser(payload.userId));
}

It's not at all clear why you've written such round-about promise code either, but I assume this is a trimmed-down example? Why are you resolving with payload instead of just calling FindUser in that function, e.g.

function MyServiceProblem(payload) {
    payload.userId = 'blah';
    return FindEvent(payload.eventId)
        .then((event) => {
            payload.userId = 'test';

            return FindUser(payload.userId);
        });
}

Upvotes: 3

Related Questions