Cancelation without Breaking a Promise

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二05 | 时间 2017 | 作者 红领巾 ] 0人收藏点击收藏
Cancelation without Breaking aPromise

Reflecting on what was so tricky about cancelable Promises, embracing functional purity as asolution

So… “cancelable Promises” have been, well, canceled . And so, the javascript community is left once again wondering how newer, Promise-based apis like fetch can possibly move forward. Why has this been such a blocker? And what can we do?

Working with the future in thepresent

Let’s start with a quick recap of the problem space. We could talk about the “event loop” in Javascript here… but for our purposes let’s just reflect on the fact that all the code in our applications executes in a specific order of operation: inside-to-outside, first line to last. Anything that takes time to accomplish “blocks” the rest of the application until it’s complete.

Obviously, this makes activating and responding to truly asynchronous actions a bit tricky. The first and most natural pattern that can help handle this in user-level code is callbacks: you pass some function another function that you want to be called with a result once some other interface declares that a result is available. There’s nothing even inherently asynchronous about this pattern: it’s just basic higher-order functions at play:

All synchronous, but with the same layer of abstraction in place that asynchronous code would require

As it happens, actual non-blocking asynchronous behavior is, in fact, almost impossible to create or imagine in javascript without using special, language-level functions that offer VM-level support for it. So let’s quickly think through one of the simplest ones that the language (at least in a browser) offers: setTimeout .

//setTimeout:: Function -> Int -> a setTimeout(x=>console.log(x), 700, 5);

In this case, we just specify a callback function, an amount of time to wait before calling it, in milliseconds, and, optionally, the value to call the callback with. Given all this, the language will wait the appropriate amount of time then then call the function with the value.

That’s it. Basic. There’s no polyfill for non-blocking setTimeout that I know of: either the language can do that (that is: wait, without blocking the execution of any subsequent lines of code) or it can’t.

But one structural limitation with setTimeout is the “dead-end” nature of its API: the side-effecting callback is not particularly extensible. That is, if you want to extend and chain further computations onto the result of the callback, you can’t do so after defining the core callback operation itself. You’d have to just get everything you want to have happen ready and all packaged up inside the callback in the first place, before handing it off to setTimeout to be executed. Now, the callback could be made to trigger other functions that are already defined in an outer scope… or even to schedule them to run later using another setTimeout . But the fact remains that the actual return value returned from the callback doesn’t really GO anywhere:

setTimeout(x=>x, 700, 5);

Above, our callback function returns a 5 as its result, but it’s basically pointless: the callback is defined synchronously (in the present) but it runs asynchronously (in the future). And so there’s no place for the “5” to go: nothing else is or can be defined for it to feed into. Nothing “else” exists in its future timeframe to listen to it.

Can we introduce this concept of “future listening”? Certainly! And that’s how we get Promises. Promises are like a representation of a value that will exist in the future… and thus provide you with an intelligible way to code against those missing values in the present. They do this by sort of “boxing up” a future event: containing an eventual explosion.

Cancelation without Breaking a Promise

Thus, when you construct a Promise, you define a function that internally executes something like a setTimeout operation but then also specifies how to capture that result by calling one of two specialized functional handlers: resolve or reject. Like this:

Hopefully I don’t need to go into the api of Promises too much. Instead, let’s call out a few key things that will quickly become relevant to our discussion of cancelation:

The constructor function (the one that’s passed the special resolve/reject arguments that control the Promise’s internal state) didn’t actually return anything (so, implicitly, it returns undefined ). This is normal for promises just like it was for setTimeout . Even if it does return something, it doesn’t matter . new Promise returns, well, a Promise: not whatever arbitrary result the constructor returned. As we said, the constructor function is executed immediately , as soon as the operation is defined. If you immediately chain a .then() method onto a promise, the result is a new, derivative Promise whose eventual state depends on the outcome of the first. This second, derivative Promise has no (and really, should not have any) hook back “into,” or control over, the original promise we created using the constructor! It simply derives its own “resolve” execution from the outcome of the former one. This channel of communication has room for only one thing, and in one direction: the value passed into the original resolve callback function, which is then farmed out to whatever function is attached to the promise using .then() .

I lied a tiny bit in #3 of course: there IS another channel of communication: reject / .catch() But reject doesn’t really change the key details of the story that much: if an operation rejects, that just feeds a value into the reject handler of the next .catch() operation. Same as resolve did. And, same as resolve, it’s a function called with just a single value. There’s not a lot of room for extra signals in that pipeline! And that’s actually a very good thing, because we’re already dealing with a complex construct we’re trying to make simple & concrete.

Forestalling thefuture

But now we get to the meat of the matter: cancelation. What if we decide at some point that we don’t care about the original operation while we’re still in the middle of it? We’d expect two critical things to happen:

whatever operation was asynchronously generating a result should stop, freeing up any resources it was consuming none of the side-effects depending on that result (or were, at least, waiting until it arrived) should ever run

This seems deceptively simple, right?

And working with just a bare setTimeout , it is actually pretty easy. While it might be an interface for creating asynchronous effects, setTimeout does return something immediately as soon as it’s called: a unique timing id. And if you’ve ever worked with setTimeout , you know that y

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

主题: Java
本文标题:Cancelation without Breaking a Promise

技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(30)