最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Optional Promise API - Stack Overflow

programmeradmin10浏览0评论

I have a validation library that is purely synchronous but is often used as part of a chain of async functions. However, I have to maintain an existing synchronous API and would like to make the promise API optional.

Can I somehow detect (at runtime) whether a function is part of a Promise chain?

This is pretty easy with callbacks since you can just check if a callback was passed in. I understand that I could pass in an optional promise boolean, but that seems inelegant.

I've also considered doing a callback interface and using a library to convert the callback interface to a promise based interface on the fly. However, I'm working in Haxe and I would prefer to keep the transforms/abstractions down to a minimum.

I also understand that you can sandwich regular functions in-between promises, but there are some instances where the behavior would differ between the two.

FINAL Edit Lots of confusion as to why I can't just return the same values, first example (below) didn't seem to help. Remember, this is still simplified.

//mix of sync with promise 
new Promise(function(resolve, reject){
  var safeToAdd = thingTracker.preflight(newThing);
  if(safeToAdd){
    return client.request.addThing(newThing); //send request to server
  } else {
    reject(newThing.errorMessages); //requires explicit reject, cannot just pass results along
  }
}).then(function(newThing){ //client and server both cool with newThing?
  thingTracker.save(newThing);
}).catch(function(errorMessages){ //handles errorMessages from client and server
  ui.show(errorMessages);
});

//pure promise
thingTracker.preflight(newThing).then(function(){
  return client.request.addThing(newThing); //sends request to server
}).then(function(newThing){  //client and server both cool with newThing?
  thingTracker.save(newThing);
}).catch(function(errorMessages){ //handles errorMessages from client and server
  ui.show(errorMessages);
});

(Old) Edit to clarify (but didn't really):

function preflight(thing){
  var validity = thing === 42;

  if(promise){
    if(validity){
      return Promise.resolve(validity);
    } else {
      return Promise.reject(validity);
    }
  } else {
    return validity;
  }
}

Obviously I can do the same checks in a then's anon function, but that is not much better than using the sync interface directly. Also note that this is a very simple example, in the real function there are side effects on thing and messages are produced.

Edit Just to illustrate my point a bit better, here is a gist of what things look like.

I have a validation library that is purely synchronous but is often used as part of a chain of async functions. However, I have to maintain an existing synchronous API and would like to make the promise API optional.

Can I somehow detect (at runtime) whether a function is part of a Promise chain?

This is pretty easy with callbacks since you can just check if a callback was passed in. I understand that I could pass in an optional promise boolean, but that seems inelegant.

I've also considered doing a callback interface and using a library to convert the callback interface to a promise based interface on the fly. However, I'm working in Haxe and I would prefer to keep the transforms/abstractions down to a minimum.

I also understand that you can sandwich regular functions in-between promises, but there are some instances where the behavior would differ between the two.

FINAL Edit Lots of confusion as to why I can't just return the same values, first example (below) didn't seem to help. Remember, this is still simplified.

//mix of sync with promise 
new Promise(function(resolve, reject){
  var safeToAdd = thingTracker.preflight(newThing);
  if(safeToAdd){
    return client.request.addThing(newThing); //send request to server
  } else {
    reject(newThing.errorMessages); //requires explicit reject, cannot just pass results along
  }
}).then(function(newThing){ //client and server both cool with newThing?
  thingTracker.save(newThing);
}).catch(function(errorMessages){ //handles errorMessages from client and server
  ui.show(errorMessages);
});

//pure promise
thingTracker.preflight(newThing).then(function(){
  return client.request.addThing(newThing); //sends request to server
}).then(function(newThing){  //client and server both cool with newThing?
  thingTracker.save(newThing);
}).catch(function(errorMessages){ //handles errorMessages from client and server
  ui.show(errorMessages);
});

(Old) Edit to clarify (but didn't really):

function preflight(thing){
  var validity = thing === 42;

  if(promise){
    if(validity){
      return Promise.resolve(validity);
    } else {
      return Promise.reject(validity);
    }
  } else {
    return validity;
  }
}

Obviously I can do the same checks in a then's anon function, but that is not much better than using the sync interface directly. Also note that this is a very simple example, in the real function there are side effects on thing and messages are produced.

Edit Just to illustrate my point a bit better, here is a gist of what things look like.

Share Improve this question edited May 4, 2020 at 8:35 vsync 131k59 gold badges340 silver badges423 bronze badges asked Dec 1, 2015 at 23:03 IndoleringIndolering 3,15232 silver badges46 bronze badges 3
  • Are you asking how to detect if a function returns a promise, or how to decide if your function should return a promise? – TbWill4321 Commented Dec 1, 2015 at 23:05
  • If my function should return a promise. – Indolering Commented Dec 1, 2015 at 23:07
  • "a validation library that is purely synchronous" - then leave it synchronous. There's no reason to use promises on your side (rather, there are reasons not to use promises). It doesn't matter where the call es from, synchronous functions are trivial to integrate in asynchronous chains just as well. – Bergi Commented Dec 2, 2015 at 3:05
Add a ment  | 

3 Answers 3

Reset to default 8

Synchronous functions can be used within a promise chain just fine. If you have a synchronous API call that is called like this:

var info = myApi("foo");

That can be used just fine within a promise chain:

someAsyncCall().then(myApi).then(someOtherFunction)

You don't need to make myApi async or return a promise to be used this way. The only thing you can't do in a promise chain with a synchronous function is that it can't be the first item in the chain, but then it doesn't need to be. Since it's synchronous, it can just be called before you start the chain if anyone wants it to execute first. Or, worst case, you can do:

Promise.resolve().then(myAPI).then(someOtherFunction);

Can I somehow detect (at runtime) whether a function is part of a Promise chain?

No, you cannot (unless you explicitly pass it some info that tells it that) and you do not need to in order to use it in a promise chain. You do not need to return a promise to be a .then() handler. You can just return a value and that value will bee the value of the promise chain at that point in the chain.

I also understand that you can sandwich regular functions in-between promises, but there are some instances where the behavior would differ between the two ... such as returning false vs throwing a message.

It is not clear to me why the behavior would have to be different. If returning false is your normal synchronous behavior, you can do that just fine in a promise chain - the next step in the chain just needs to handle the false value that is passed to it, just like the next line of synchronous code would do. If throwing an exception is your normal behavior, you can do that just fine in a promise chain too (the promise chain will turn the exception into a rejection) which the following code can decide how it wants to handle. One can make a good argument that it would be worse if your synchronous function behaved differently based on whether it was part of a promise chain or not. If you see a good reason for two different behaviors, then you should probably have either two different functions (that can be documented differently) or an option passed in that determines the behavior.

Comment based on the code you added in your edit

You seem to think that when called via a promise chain that you need to return a resolved or rejected promise chain. You do not need to do that. You can just return a normal value and the promise chain will inherit the value you return. There is really no reason to return a resolved or rejected promise unless you want it to be the first function in a promise chain, but in that case, it isn't in a promise chain yet anyway so you could never detect that. And, there's no reason for a synchronous operation to be the first one in a promise chain. Just call the synchronous operation first and then initiation your promise chain with the async operations.

Here's a demo of a synchronous function being used in a promise chain:

function log(str) {
    var div = document.createElement("div");
    div.innerHTML = str;
    document.body.appendChild(div);
}

// test async call
function delay(t, val) {
    return new Promise(function(resolve) {
        setTimeout(function() {
            resolve(val);
        }, t);
    });
}

function square(x) {
  return x * x;
}

log("Calculating the square of 9");
delay(500, 9).then(square).then(function(result) {
    log("Result = " + result);
});

Or, you can even do this to initiate a promise chain with your synchronous operation as the first operation in the chain:

function log(str) {
    var div = document.createElement("div");
    div.innerHTML = str;
    document.body.appendChild(div);
}

function square(x) {
  return x * x;
}

log("Calculating the square of 9");
Promise.resolve(9).then(square).then(function(result) {
    log("Result = " + result);
});

I believe promises have only one argument so it's not possible to pass an isPromise boolean through the chain, even if you could, you'd have to consciously add the parameter, so why not just call the async function...

function preflight(thing){
  return thing === 42;
}

function preflightAsync(thing){
  return new Promise(resolver);
  function resolver(resolve,reject){
    setTimeout(function foo(){
      preflight(thing) ? resolve(true,"this won't make it") : reject();
    },1000);
  }
}

// Promised
preflightAsync(42).then(doSomethingWithIt).catch(doSomethingWithIt);
preflightAsync(89).then(doSomethingWithIt).catch(doSomethingWithIt);

// Call Directly
doSomethingWithIt(preflight(42));
doSomethingWithIt(preflight(89));

function doSomethingWithIt(result,nada){
  result = result ? "BAM!!!" : "WAM!!!";
  console.log("doSomethingWithIt",result,nada);//doSomethingWithIt BAM!!! undefined
  return result;
}

These examples use the new native Promise function, browser support is limited.

As we can see from previous answers you can't use exactly the same function also for Promises. Adding my workaround here which is unfortunately extending the function prototype ...

The problem is that if you want to use the same function synchronously and in a Promise the use of arguments would differ.

As an example - a function truncating a string:

sync:

truncate('A very long string ing from a sync. operation', 10);

async:

Promise.resolve('A very long string ing from a network request')
  .then( truncate(10) )

The async version would make the truncate Function think that 10 is the string and you would need to check typeof in every function or do ducktyping. What would work:

Promise.resolve('A very long string ing from a network request')
  .then( truncate.it(10) )

if you extended the function prototype before:

Function.prototype.it = (function() {
  return function() {
    var args = arguments;
    return function(){
        Array.prototype.push.apply(arguments, args);
        return this.apply(null, arguments);
    }.bind(this);
  };
}());

function foo(a, b, c) {
  console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);
};

// LOGIC :    
foo(1, 2, 3);
// SAME AS
foo.it(2, 3)(1);
// OR
foo.it(3)(1, 2);
// OR pretty useful for .then()
Promise.resolve(1).then(foo.it(2, 3));

fiddle

发布评论

评论列表(0)

  1. 暂无评论