可选的Promise API

时间:2015-12-01 23:03:44

标签: javascript promise

我有一个纯粹同步的验证库,但通常用作异步函数链的一部分。但是,我必须维护一个现有的同步API,并希望将promise API作为可选项。我可以以某种方式检测(在运行时)函数是否是Promise链的一部分吗?

使用回调这很容易,因为你可以检查是否传入了一个回调。我知道我可以传递一个可选的promise boolean,但这看起来不太优雅。

我还考虑过做一个回调接口并使用库将回调接口转换为基于promise的接口。但是,我在Haxe工作,我宁愿将变换/抽象降到最低。

我也明白你可以将承诺之间的常规函数​​夹在中间,但在某些情况下,两者之间的行为会有所不同。

最终修改关于为什么我不能只返回相同的值,第一个例子(下面)似乎没有帮助的许多混乱。请记住,这仍然是简化的。

func getImagesAtInterval(url: NSURL){

    let asset : AVURLAsset = AVURLAsset(URL: url, options: nil)
    let duration : CMTime = asset.duration
    let totalTime = Int(CMTimeGetSeconds(duration) * 1000)
    let assetGenerator : AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)

    imageSet = [UIImage]()
    var value = 0

    for var i : Int = 0; i < totalTime; i+=200 {

        let time : CMTime = CMTimeMake(Int64(value), 1000)
        var imageRef : CGImageRef!
        do {
            imageRef = try assetGenerator.copyCGImageAtTime(time, actualTime: nil)
        } catch let error as NSError {
            print("Image generation failed with error \(error)")
        }
        if let image : UIImage? = UIImage(CGImage: imageRef){
            dispatch_async(dispatch_get_main_queue()) {

//I would like to see the images in the previewImageView 
//change as the loop operates.
                self.previewImageView.image = image
            }
            imageSet.append(image!)
        }           
        value += 200
    }
}

(旧)修改以澄清(但实际上并非如此):

//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);
});

显然我可以在function preflight(thing){ var validity = thing === 42; if(promise){ if(validity){ return Promise.resolve(validity); } else { return Promise.reject(validity); } } else { return validity; } } 的匿名函数中进行相同的检查,但这并不比直接使用同步接口好多少。另请注意,这是一个非常简单的示例,在实际函数中,then会产生副作用并生成消息。

修改为了更好地说明我的观点,这里的内容是gist

3 个答案:

答案 0 :(得分:6)

可以在promise链中使用同步函数。如果您有一个这样调用的同步API调用:

var info = myApi("foo");

在承诺链中可以很好地使用它:

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

您不需要使myApi异步或返回以这种方式使用的承诺。在具有同步函数的promise链中,你唯一不能做的就是它不能是链中的第一个项,但它不需要。由于它是同步的,所以只要有人想要它首先执行,就可以在启动链之前调用它。或者,最糟糕的情况是,你可以这样做:

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

我可以以某种方式检测(在运行时)函数是否是a的一部分   承诺链?

不,你不能(除非你明确地传递了一些告诉它的信息),你不需要为了在诺言链中使用它。您不需要将promise作为.then()处理程序返回。您只需返回一个值,该值将成为链中该点的promise链值。

  

我也明白你可以将常规功能夹在中间   承诺,但有些情况下行为会有所不同   两者之间...比如返回假与投掷消息。

我不清楚为什么这种行为必须有所不同。如果返回false是您正常的同步行为,那么您可以在承诺链中做到这一点 - 链中的下一步只需要处理传递给它的false值,就像下一行同步代码会做。如果抛出异常是你的正常行为,你也可以在promise链中做到这一点(promise链会将异常变成拒绝),以下代码可以决定它想要处理的方式。如果您的同步函数根据它是否是promise链的一部分而表现不同,那么可以提出一个很好的论据。如果你看到两个不同行为的充分理由,那么你可能应该有两个不同的函数(可以用不同的方式记录)或者传入一个决定行为的选项。

根据您在修改中添加的代码进行评论

您似乎认为通过承诺链调用时,您需要返回已解析或拒绝的承诺链。你不需要这样做。您只需返回一个正常值,promise链将继承您返回的值。除非你想让它成为promise链中的第一个函数,否则没有理由返回已解决或被拒绝的promise,但在这种情况下,它仍然不在promise链中,所以你永远无法检测到它。并且,没有理由将同步操作作为承诺链中的第一个操作。只需先调用同步操作,然后使用异步操作启动您的保证链。

这是一个在promise链中使用的同步函数的演示:

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);
});

或者,您甚至可以通过同步操作启动承诺链作为链中的第一个操作:

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);
});

答案 1 :(得分:1)

我相信promises只有一个参数,因此无法通过链传递isPromise布尔值,即使你可以,你也必须有意识地添加参数,所以为什么不调用异步功能...

&#13;
&#13;
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;
}
&#13;
&#13;
&#13;

这些示例使用新的本机Promise功能,浏览器支持有限。

答案 2 :(得分:0)

正如我们从之前的答案中看到的那样,你也不能对Promises使用完全相同的功能。在这里添加我的解决方法,遗憾的是扩展了function原型...

问题在于,如果你想同步使用同一个函数而在Promise中使用arguments会有所不同。

作为示例 - 截断字符串的函数:

同步:

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

异步:

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

异步版本会使截断函数认为10是字符串,您需要在每个函数中检查typeof或进行欺骗。 什么可行:

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

如果你之前扩展了函数原型:

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