119

A promise, just for example:

var P = new Promise(function (resolve, reject) {
  var a = 5;
  if (a) {
    setTimeout(function(){
      resolve(a);
    }, 3000);
  } else {
    reject(a);
  }
});

After we call the .then() method on the promise:

P.then(doWork('text'));

Then doWork function looks like this:

function doWork(data) {
  return function(text) {
    // sample function to console log
    consoleToLog(data);
    consoleToLog(b);
  }
}

How can I avoid returning an inner function in doWork, to get access to data from the promise and text parameters? Are there any tricks to avoiding the inner function?

cssyphus
  • 37,875
  • 18
  • 96
  • 111
user3110667
  • 1,201
  • 2
  • 9
  • 4
  • 1
    Why would anyone intentionally renounce **currying**? In order to use the hideous `bind` method? - which is also extremely slow. –  Jul 09 '16 at 20:49
  • @ftor I don't understand you, can you please provide some code for clarification? – Roland Aug 30 '17 at 07:27

6 Answers6

124

Perhaps the most straightforward answer is:

P.then(function(data) { return doWork('text', data); });

Or, since this is tagged ecmascript-6, using arrow functions:

P.then(data => doWork('text', data));

I find this most readable, and not too much to write.

jib
  • 40,579
  • 17
  • 100
  • 158
  • In your es6 example & 'classes' how would I go about replacing "text" with a reference to an item in an array I was iterating through when creating the promise. EG: `fetch(request).then(data => this.AssetRetrieved(jData.parms[i].Value, data));` – Reahreic Mar 01 '21 at 15:32
  • @Reahreic That sounds like a variation of the JS closures in a loop problem. Is [this answer](https://stackoverflow.com/a/16661498/918910) helpful? If not, please consider asking a new question with more details. – jib Mar 01 '21 at 21:31
99

You can use Function.prototype.bind to create a new function with a value passed to its first argument, like this

P.then(doWork.bind(null, 'text'))

and you can change doWork to,

function doWork(text, data) {
  consoleToLog(data);
}

Now, text will be actually 'text' in doWork and data will be the value resolved by the Promise.

Note: Please make sure that you attach a rejection handler to your promise chain.


Working program: Live copy on Babel's REPL

function doWork(text, data) {
  console.log(text + data + text);
}

new Promise(function (resolve, reject) {
    var a = 5;
    if (a) {
      setTimeout(function () {
        resolve(a);
      }, 3000);
    } else {
      reject(a);
    }
  })
  .then(doWork.bind(null, 'text'))
  .catch(console.error);
Ben Everard
  • 13,652
  • 14
  • 67
  • 96
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
  • thanks, that helps, earlier i try doWork.call(this, 'text'), but data was replaced by 'text' – user3110667 Oct 02 '15 at 17:34
  • 2
    `call` invokes a function in-place, `bind` creates a new function, however both accept an execution context as their first argument. – sdgluck Oct 02 '15 at 18:15
5

Use currying.

var P = new Promise(function (resolve, reject) {
    var a = 5;
    if (a) {
        setTimeout(function(){
            resolve(a);
        }, 3000);
    } else {
        reject(a);
    }
});

var curriedDoWork = function(text) {
    return function(data) {
        console.log(data + text);
    }
};

P.then(curriedDoWork('text'))
.catch(
    //some error handling
);
yks
  • 79
  • 1
  • 6
  • b careful with this, if you create `curriedDoWork` into a promise by doing `return new Promise()` on the first line of this function, the promise is executed as soon as you call `curriedDoWork()` (like you do in `..then(curriedDoWork('text'))` – Flame Mar 23 '18 at 17:39
  • @Flame: short answer, for your convenience you can wrap the promise into a function, if you like to do so. – germain May 04 '18 at 23:41
  • @yks, you could have indicated this syntax which is quite interesting const curriedWork = text => data => console.log(data + text) – germain May 04 '18 at 23:48
  • 1
    @germain ah yes i have seen this form before, gotta love functional programming. However i experience arrow functions break in some browser, so i tend to avoid it now. – yks May 06 '18 at 23:43
  • @yks, only Internet Explorer doesn't support it and will never, because of Edge, Internet Explorer last build was December 9th 2015. Let's move on ~ – germain May 08 '18 at 02:15
3

The new answer to this question is to use arrow functions (which automatically bind the this and are much more readable). Google for links such as: https://2ality.com/2016/02/arrow-functions-vs-bind.html

You can set the text like:

this.text = 'text';
P.then(data => doWork(data));

Note: this.text inside doWork will evaluate to 'text'.

This is suggested by jib above and that (or this!) should be the accepted answer now.

cssyphus
  • 37,875
  • 18
  • 96
  • 111
giwyni
  • 2,928
  • 1
  • 19
  • 11
0

Lodash offers a nice alternative for this exact thing.

 P.then(_.bind(doWork, 'myArgString', _));

 //Say the promise was fulfilled with the string 'promiseResults'

 function doWork(text, data) {
     console.log(text + " foo " + data);
     //myArgString foo promiseResults
 }

Or, if you'd like your success function to have only one parameter (the fulfilled promise results), you can utilize it this way:

P.then(_.bind(doWork, {text: 'myArgString'}));

function doWork(data) {
    console.log(data + " foo " + this.text);
    //promiseResults foo myArgString
}

This will attach text: 'myArgString' to the this context within the function.

JellyRaptor
  • 725
  • 1
  • 8
  • 20
0

use this so you can access global variable inside the promise body

var ref=this;

Example

p.then((data)=>{
  var ref=this;
});
aryan singh
  • 807
  • 2
  • 8
  • 18