How to create a function that returns an existing promise instead of new promise?

自古美人都是妖i 提交于 2019-12-17 20:58:10

问题


JavaScript/Promise experts,

I hope you can help me, because I don't understand how I can create a function that returns an existing promise instead of a new promise. I have done a lot of research, but all examples return a new promise every time the function is called.

Assume I have a function that takes some time to process and generates a different result every time it is being called. When I use the new promise method then I still need to wait to complete every time and I don't get the same value if I recall the function.

I have included proof of concept I have created that works as desired (unless I have overlooked something). I think this "solution" is ugly, because I need to move variables to a higher scope and I still need to create a new promise every time the function is called.

My assumption at this moment; to make this nice code the function needs to return an existing promise instead of a new one, but if other solutions provide the same functionality and nicer code then please let me know. Native ES6 is preferred.

Thanks!

-

'use strict';

var p;
var q;

var randomNumber = function() {
  return new Promise(function(resolve) {
    console.log(`p: ${p}`);

    if (typeof p === 'undefined') {
      setTimeout(function() {
        p = Math.random();
        resolve(p);
      }, 1000);

    } else {
      resolve(p);
    }
  });
};

var randomNumberPlusOne = function() {
  return new Promise(function(resolve) {
    console.log(`q: ${q}`);

    if (typeof q === 'undefined') {
      randomNumber()
        .then(function(p) {
          q = p + 1;
          resolve(q);
        });

    } else {
      resolve(q);
    }
  });
};

var showResult = function(result) {
  console.log();
  console.log(`r: ${result}`);
};

randomNumber()
  .then(showResult);

randomNumber()
  .then(randomNumberPlusOne)
  .then(showResult);

randomNumberPlusOne()
  .then(showResult);

setTimeout(function() {
  console.log();
  console.log('setTimeout:');
  console.log();
  randomNumber()
    .then(showResult);

  randomNumber()
    .then(randomNumberPlusOne)
    .then(showResult);

  randomNumberPlusOne()
    .then(showResult);
}, 2000);

- (code below is based on the feedback from Bergi;

'use strict';

var randomNumber = (function() {
  var p = null;

  return function() {
    console.log('p:');
    console.log(p);
    console.log();

    if (!p) {
      p = new Promise(function(resolve) {
        setTimeout(function() {
          resolve(Math.random());
        }, 1000);
      });
    }

    return p;
  };
})();

var randomNumberPlusOne = (function() {
  var q = null;

  return function() {
    console.log('q:');
    console.log(q);
    console.log();

    if (!q) {
      q = randomNumber()
        .then(function(p) {
          return p + 1;
        });
    }

    return q;
  };
})();

var showResult = function(result) {
  console.log(`r: ${result}`);
  console.log();
};

randomNumber()
  .then(showResult);

randomNumber()
  .then(randomNumberPlusOne)
  .then(showResult);

randomNumberPlusOne()
  .then(showResult);

setTimeout(function() {
  console.log();
  console.log('setTimeout:');
  console.log();

  randomNumber()
    .then(showResult);

  randomNumber()
    .then(randomNumberPlusOne)
    .then(showResult);

  randomNumberPlusOne()
    .then(showResult);

}, 2000);

回答1:


You'll want to memoise the promise, not the value that it resolves with. Memoisation works fine with promises as result values.

var p = null;
function notSoRandomAsyncNumber() {
  if (!p)
    p = new Promise(function(resolve) {
      setTimeout(function() {
        resolve(Math.random());
      }, 1000);
    });
  return p;
}

Or, abstracted into a helper function:

function memoize(fn) {
  var cache = null;
  return function memoized(args) {
    if (fn) {
      cache = fn.apply(this, arguments);
      fn = null;
    }
    return cache;
  };
}
function randomAsyncNumber() {
  return new Promise(res => {
    setTimeout(() => resolve(Math.random()), 1000);
  });
}
function randomAsyncNumberPlusOne() {
  return randomAsyncNumber().then(n => n+1);
}
var notSoRandomAsyncNumber = memoize(randomAsyncNumber);
var notSoRandomAsyncNumberPlusOne = memoize(randomAsyncNumberPlusOne);

(notice that notSoRandomAsyncNumberPlusOne still will create a randomAsyncNumber() on the first call, not a notSoRandomAsyncNumber())




回答2:


Try the new variable approach suggested. Promises are designed to be single-shot, so it depends on the implementation. What you are trying to do is more in the arena of events or reactivex. If you are using jquery then you can use their event scheme. A cleaner one is available on the nodejs site. Since it does not require anything else it runs in the browser. rxjs is the cream of the stream processing crop, but it is a big library and requires some learning. Worth it - since the same knowledge is useful client and server in many languages. You can even set up a stream from a promise - among many other ways.



来源:https://stackoverflow.com/questions/31820133/how-to-create-a-function-that-returns-an-existing-promise-instead-of-new-promise

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!