Promessas, passe parâmetros adicionais para então encadear

100

Uma promessa, apenas por exemplo:

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

Depois de chamarmos, o método da promessa:

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

A função doWork é semelhante a esta:

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

Como posso evitar o retorno de uma função interna em doWork para obter acesso aos dados dos parâmetros de promessa e texto? Existem truques para evitar a função interna?

user3110667
fonte
1
Por que alguém renunciaria intencionalmente ao currying ? Para usar o bindmétodo horrível ? - que também é extremamente lento.
@ftor Não estou entendendo, você pode fornecer algum código para esclarecimento?
Roland

Respostas:

86

Você pode usar Function.prototype.bindpara criar uma nova função com um valor passado para seu primeiro argumento, como este

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

e você pode mudar doWorkpara,

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

Agora, textestará realmente 'text'dentro doWorke dataserá o valor resolvido pela Promessa.

Observação: certifique-se de anexar um manipulador de rejeição à sua cadeia de promessa.


Programa de trabalho: cópia ao vivo no REPL do Babel

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);
os quatro olhos
fonte
obrigado, isso ajuda, antes eu tentei doWork.call (this, 'text'), mas os dados foram substituídos por 'text'
user3110667
2
callinvoca uma função no local, bindcria uma nova função, porém ambos aceitam um contexto de execução como seu primeiro argumento.
sdgluck
103

Talvez a resposta mais direta seja:

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

Ou, já que está marcado ecmascript-6, usando as funções de seta:

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

Acho isso mais legível e não muito para escrever.

bujarrona
fonte
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
fonte
b cuidado com isso, se você criar curriedDoWorkuma promessa ao fazer return new Promise()na primeira linha desta função, a promessa será executada assim que você chamar curriedDoWork()(como você faz em..then(curriedDoWork('text'))
Flame
@Flame: resposta curta, para sua conveniência, você pode embrulhar a promessa em uma função, se desejar.
germain
@yks, você poderia ter indicado esta sintaxe que é bastante interessante const curriedWork = text => data => console.log (data + text)
germain
1
@germain ah sim, eu já vi esse formulário antes, tenho que amar a programação funcional. No entanto, percebo que as funções das setas quebram em alguns navegadores, por isso tendo a evitá-las agora.
yks
@yks, apenas o Internet Explorer não oferece suporte e nunca será, por causa do Edge, a última compilação do Internet Explorer foi em 9 de dezembro de 2015. Vamos continuar ~
germain
0

Lodash oferece uma boa alternativa para exatamente isso.

 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
 }

Ou, se desejar que sua função de sucesso tenha apenas um parâmetro (os resultados da promessa cumprida), você pode utilizá-la desta forma:

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

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

Isso se conectará text: 'myArgString'ao thiscontexto dentro da função.

JellyRaptor
fonte
0

A nova resposta a essa pergunta é usar as funções de seta, que vinculam automaticamente o 'this' e são muito mais legíveis. Google para links como: https://2ality.com/2016/02/arrow-functions-vs-bind.html

Você pode definir o texto como: this.text = 'text' P.then (data => doWork (data)); //this.text dentro do doWork será avaliado como 'texto'.

Isso é sugerido pelo jib acima e que (ou essa!) Deve ser a resposta aceita agora.

giwyni
fonte