piezontm
piezontm

Reputation: 103

Difficulties with Bluebird Promise.bind()

I'm trying to call a chain of promises sequentially. I would like to pass some state across all the promises. I thought I'd be able to bind() my state but have been unable to get it working. My sample code looks like:

var Promise = require('bluebird');
...
function op1(state) {
    return new Promise(function(resolve, reject) {
        this.num += 1;
        resolve();
    });
}
function op2(state) {
    return new Promise(function(resolve, reject) {
        this.num += 1;
        resolve();
    });
}
function op3(state) {
    return new Promise(function(resolve, reject) {
        this.num += 1;
        resolve();
    });
}

function sequence(tasks, state) {
    var current = Promise.cast();
    for (var k = 0; k < tasks.length; ++k) {
        current = current.thenReturn().then(tasks[k]);
    }
    return current.thenReturn();
}

var state = { yadda: "yadda",
                      num:   0 };
var q = [ op1, op2, op3 ];

var p   = sequence( q, state );
var ret = p()
            .then( function(val) {
                console.log(val);
            })
            .catch ( function(val) {
                console.error(val);
            });

I've tried to simplify the problem to what I see in many example:

op1().bind(state).then(op2).then(op3); 

However, this isn't working either.

I've also tried to change sequence() to the following (among various other things):

var current = Promise.cast().bind(state);

I am still very new to promises. I suspect I am missing something easy. Any advice or help would be much appreciated.

Upvotes: 1

Views: 603

Answers (1)

Ok, let's see what could be done.

We could start with binding state to each task.

function sequence(tasks, state) {
    var current = Promise.resolve();
    for (var k = 0; k < tasks.length; ++k) {
        const taskWithState = tasks[k].bind(state);
        current = current.then(taskWithState);
    }
    return current;
}

I've used bind to set this inside tasks to state, so:

function op1() {
    // this === state
    return new Promise(function(resolve) {
        // this === window
        this.num += 1;
        resolve();
    });
}

As You might expect, a function that we pass to Promise creates its own context, so this would not point to state like we want it to.

To bypass that issue we could either do an old trick with saving context in a variable (like var that = this) or we could just use arrow functions, which do not create their own context. With that in mind:

function op1() {
    // this === state
    return new Promise(resolve => {
        // this === state
        this.num += 1;
        resolve();
    });
}

All together now

function op1() {
    return new Promise(resolve => {
        this.num += 1;
        resolve();
    });
}

function op2() {
    return new Promise(resolve => {
        this.num += 1;
        resolve();
    });
}

function op3() {
    return new Promise(resolve => {
        this.num += 1;
        resolve();
    });
}

function sequence(tasks, state) {
    let current = Promise.resolve();
    for (let k = 0; k < tasks.length; ++k) {
        const taskWithState = tasks[k].bind(state);
        current = current.then(taskWithState);
    }
    return current;
}

var state = { yadda: "yadda", num:  0 };
var q = [ op1, op2, op3 ];

sequence(q, state)
  .then(() => console.log(state))
  .catch(err => console.error(err));

Upvotes: 1

Related Questions