Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What's the point of Promises?

2025-02-25 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

What is the focus of Promises? in view of this question, this article introduces in detail the corresponding analysis and solutions, hoping to help more partners who want to solve this problem to find a more simple and feasible way.

So far, probably every JavaScript developer and their grandmother has heard of Promises. If you don't, then you will. The concept of promises is proposed by members of the CommonJS team in the Promises/A specification. Promises have come to be used as a way to manage callbacks for asynchronous operations, but they are far more useful than that by design. In fact, because of their multiple uses, countless people have told me-- after I wrote something about promises-- that I "missed the point of promises." So what is the focus of promises?

A little bit about Promises.

Before I start the "focus" of promise, I think I should give you a little insight into how they work. A promise is an object-- according to the Promise/A specification-- only one method is needed: then. The then method takes three parameters: a successful callback, a failed callback, and a forward callback (the specification does not require the implementation of forward callbacks, but many of them are implemented). A brand new promise object is returned from each call to then.

A promise can be one of three states: unfinished, completed, or failed. Promise starts in an unfinished state. If it succeeds, it will be finished, and if it fails, it will be a failure. When a promise moves to the finished state, all successful callbacks registered with it will be called and the successful result value will be passed to it. In addition, any successful callback registered with promise will be called immediately after it has been completed.

The same thing happens when promise moves to a failed state, except that it calls a failure callback instead of a success callback. For an implementation that contains forward features, promise can update its progress at any time before it leaves the unfinished state. When progress is updated, all forward callbacks (progress callbacks) are passed with the value of progress and called immediately. Forward callbacks are handled differently from success and failure callbacks; if you register a forward callback after a progress update has occurred, the new forward callback will only be called by the updated progress after it has been registered.

We won't delve further into how promise state is managed, because that's not within the specification, and each implementation is different. In later examples, you will see how it is done, but for now that's all you need to know.

Handle callback

Handling callbacks for asynchronous operations, as mentioned earlier, is the most basic and common use of promises, so let's compare a standard callback to a callback that uses promise.

Callback

/ / Normal callback usage asyncOperation (function () {/ / Here's your callback}); / / Now `asyncOperation` returns a promise asyncOperation () .then (function () {/ / Here's your callback})

I doubt if anyone would really care about using promises just by looking at this example. It doesn't seem to do any good, except that "then" makes it more obvious that the callback function is called after the asynchronous operation is completed. But even with this benefit, we now have more code (abstractions should make our code shorter, shouldn't we? And promise performs slightly worse than the standard callback

But don't let it get in your way. If this was the best thing promise could do, this article would not exist.

The pyramid of doom

You can find many articles online quoting "Pyramid of doom" as the main reason for using promises. This means that multiple asynchronous operations need to be performed continuously. Under normal callbacks, we will end nested calls between calls to each other; as the invocation code becomes more indented, a pyramid (pointing to the right) is generated and hence the name "pyramid of doom". This is not so bad if you only need to perform one or two asynchronous operations in a row, but once you need to do three or more, it will become difficult to read, especially if there is a considerable amount of processing to be done at each step. Using promises can help flatten the code and make it easier to read again. Let's see.

The pyramid of doom

/ / Normal callback usage = > PYRAMID OF DOOOOOOOOM asyncOperation (function (data) {/ / Do some processing with `data`anotherAsync (function (data2) {/ / Some more processing with `data2` yetAnotherAsync (function () {/ / Yay we're finished!});}); / / Let's look at using promises asyncOperation () .then (function (data) {/ / Do some processing with `data`return anotherAsync () }) .then (function (data2) {/ / Some more processing with `data2` return yetAnotherAsync ();}) .then (function () {/ / Yay we're finished!})

As you can see, the use of promises makes things flatter and more readable. This works because-- as mentioned earlier-- then returns a promise, so you can keep concatenating then calls. The promise returned by then loads the value returned by the call. If the call returns a promise (as in this example), the promise returned by then is loaded with the same value as the promise returned by your callback. There is a lot of content, so I will help you understand it step by step.

The asynchronous operation returns a promise object. So we call then in that promise object and pass it a callback function; then also returns a promise. When the asynchronous operation ends, it will load the promise with data. Then the callback is called and the data is passed as a parameter. If the callback does not contain a return value, the promise returned by then will be assembled immediately without a value. If the callback does not return a promise, then the promise returned by then will load that value immediately. If the callback returns a promise (as in the example), the promise returned by then will wait until the promise returned by our callback is fully loaded. Once the promise of our callback is loaded, its loaded value (in this case, data2) will be submitted to the promise of then. Then the promise in then loads the data2. Wait. It sounds a little complicated, but it's actually very simple. If you can't understand what I said, I'm very sorry. I guess I may not be the best person to talk about it.

Replace with a named callback

But obviously promises is not the only way to flatten this structure. After writing a post that mentioned that promises solved the pyramid problem of doom, one person commented on the post.

I think promises is sometimes useful, but the problem of "nested" callbacks (Christmas tree syndrome) can be handled as usual with a named function as an argument instead of an anonymous function:

AsyncCall (param1, param2, HandlerCallback)

Function HandlerCallback (err, res) {

/ / do stuff

}

Its example only gives a deep example, but it is still correct. Let's extend my previous example to make this look easier.

Named callback

/ / Normal callback usage = > PYRAMID OF DOOOOOOOOM asyncOperation (handler1); function handler1 (data) {/ / Do some processing with `data`anotherAsync (handler2);} function handler2 (data2) {/ / Some more processing with `data2`yetAnotherAsync (handler3);} function handler3 () {/ / Yay we're finished!}

Look at the code above! They are absolutely right! It's just a flat structure, but there's a problem here as well in the old callback examples that I've never noticed before: dependency and reusability. Dependency and reusability are reversible types that are interrelated. The less you rely on something, the more reusable it will be. In the above example, handler1 relies on handler2,handler2, depends on handler3. This means that handler1 can no longer be used for any purpose unless handler2 is also presented. What's the point of naming your function if you're not going to reuse them?

The worst part is that handler1 doesn't care what's going on in handler2. It doesn't need handler2 at all except to work asynchronously with it. So let's eliminate these dependencies and then make the function more reusable by using promise.

Chained callback

AsyncOperation (). Then (handler1). Then (handler2). Then (handler3); function handler1 (data) {/ / Do some processing with `data`return anotherAsync ();} function handler2 (data2) {/ / Some more processing with `data2` return yetAnotherAsync ();} function handler3 () {/ / Yay we're finished!}

Doesn't that look better? If other functions exist, handler1 and handler2 are not related to each other now. Want to see if they are really good? Now handler1 can be used without the need for handler2. Instead, after the handler1 is manipulated, we will be able to use another handler.

Multiplex function

AsyncOperation (). Then (handler1). Then (anotherHandler); function handler1 (data) {/ / Do some processing with `data`return anotherAsync ();} function anotherHandler (data2) {/ / Do some really awesome stuff that you've never seen before. It'll impress you}

Now handler1 has been removed from handler2 and can be used in more situations, especially those provided by handler2 that we don't want to use. This is reusability! The only way for critics to solve the readability of code is by eliminating indentation. We don't want to eliminate indentation just for indentation. Multi-level indentation is just a sign that something is wrong, and the problem is not necessarily in itself. It's like he has a headache caused by dehydration. The real problem is dehydration, not headaches. The solution is to get hydrate, not some painkillers.

Parallel asynchronous operation

In the article I mentioned earlier, I compared promises to events in handling asynchronous operations. Unfortunately, according to the comments given by those who have mentioned it, I am not very successful. I described the power of promises, and then turned to events to describe their power, as I used in my special project. There is no comparison or comparison. One commentator wrote (corrected a little grammatical error):

I think the example in the post is a bad contrast. There is a paper that proves what the value of promises will be. if you press the fictional "start server button", you will not only start a web server, but also a database server that just updates UI while they are running.

Using promise's .when method will make this "multiple asynchronous operations" example common, but responding to multiple asynchronous events requires an uncommon amount of code.

He is absolutely right. As a matter of fact, I did not compare the two situations. The point of that article is actually to show that promises is not the only mechanism for asynchronous operations, and in some cases, they are not necessarily the best. In the case pointed out by this commentator, promises is certainly the best solution. Let's see what he's talking about.

JQuery has a method called when that can take any number of promise parameters and return a single promise. If any of the promise input fails, the promise returned by when will also fail. If all promises is loaded, each value will be passed to the additional callback in the order in which promises is defined.

It is useful to perform numerous asynchronous operations in parallel, and then just continue to perform callbacks after each of them is over. Let's look at a simple example.

JQuery.when

/ / Each of these async functions return a promise var promise1 = asyncOperation1 (); var promise2 = asyncOperation2 (); var promise3 = asyncOperation3 (); / / The $refers to jQuery $.when (promise1, promise2, promise3). Then (function (value1, value2, value3) {/ / Do something with each of the returned values})

People often say that this is one of the best things that promises brings, and it is also part of the significance of promises. I also think this is a good feature that simplifies a lot of operations, but the mechanism of this when approach is not mentioned in any Promises specification at all, so I don't think it is the meaning of Promises. One specification mentions the when method, but it is completely different from the one above. As far as I know, jQuery is the only library that implements this when method. Other promises libraries, such as Q, Dojo, and when, implement the when method according to Promises/B spec, but do not implement the when method mentioned by the annotator. However, the Q library has an all method, and when.js also has a parallel method, which works the same as the jQuery.when method above, except that they accept an array type of parameters instead of any number of parameters.

Representation of values

Another commentator left me a message:

Promise is a better way to deal with the following scenarios:

"I want to find a user in this database, but the find method is asynchronous."

So, here we have a find method that does not return a value immediately. But in the end, it does "return" a value (through a callback), and you want to deal with that value in some way. Now, by using a callback, you can define a continuation section, or "some code that will deal with that value at a later time."

Promise changed the kind of "Hey, here are some codes you'll find you use to handle return values." They are methods that allow "find" to say, "Hey, I'll be busy finding the information you're looking for, but in the meantime you can continue to wait for the result to be returned, and you can process it in any way you want at the same time, just like the actual thing!"

Promise represents the real number. That's a trap. They work when you deal with Promise as if you were dealing with real things. Promise's JavaScript implementation expects you to pass it a callback function, which is just a "coincidence" and it's not important.

I believe this is really the point of promise. Why? Read the first sentence of the Promise/A specification: "an promise represents the final value returned by the completion of an operation." Makes it a little obvious, doesn't it? Well, even if that's the point, that doesn't stop me from presenting other people's opinions later in this article. Anyway, let's talk a little more about the idea.

This concept is beautiful, but how is it embodied in practice? What does it look like to think of promises as a numerical representation? First, let's take a look at some synchronized code:

Synchronous Code

/ / Search a collection for a list of "specific" items var dataArray = dataCollection.find ('somethingSpecific'); / / Map the array so that we can get just the ID's var ids = map (dataArray, function (item) {return item.id;}); / / Display them display (ids)

Well, if the dataCollection.find is synchronized, this code will work properly. But what if it's asynchronous? I mean, look at this code; it's written completely synchronously. If find is asynchronous, there is no way to guarantee that it works correctly, right? Wrong. We just need to modify the map and display to accept promises as a parameter to represent the data used for the calculation. Again, find and map need to return promises. So suppose dataCollectionque does not contain data: just call AJAX to get the data. So a promise will now be returned. Now, let's rewrite map and display and accept promises, but we have different names: pmap and pdisplay.

Accept Promise as a parameter

Function pmap (dataPromise, fn) {/ / Always assume `dataPromise` is a promise... Cuts down on the code / / `then` returns a promise, so let's use that instead of creating our own return dataPromise.then (/ / Success callback function (data) {/ / Fulfill our promise with the data returned from the normal synchronous `map` return map (data, fn);} / / Pass errors through by not including a failure callback) } function pdisplay (dataPromise) {dataPromise.then (/ / Success callback function (data) {/ / When we have the data, just send it to the normal synchronous `display` display (data);}, / / Failure callback function (err) {/ / If it fails, we'll just display the error display (err) });}

It won't be too difficult, is it? Let's now rewrite the above example with these new functions:

Asynchronous code

/ / Search a collection for a list of "specific" items var dataArray = dataCollection.find ('somethingSpecific'); / / Map the array so that we can get just the ID's var ids = pmap (dataArray, function (item) {return item.id;}); / / Display them pdisplay (ids)

All I have to do is change the names of these functions. If you are confident, you can write these functions with the same name, accept a promise or normal value, and react accordingly. In the new code, these promise represent the final value returned, so we can write the code in a way that promise looks like a real value.

I lied a little bit. I said above, "this will work exactly the same," but this is a thorny problem. Apart from the fact that the names of some functions have changed, there are some other differences: any code that appears after the call to pdisplay may be called before the actual display occurs. So you either need to make the later code independent of the end of the display, or you need to return a promise from the pdisplay and have the other code run after the promise is loaded. Part of the reason pdisplay does not return a promise in my example is that it does not have a returned value, so in this section of our discussion, promise cannot be represented as a numeric value.

In any case, you can see how to make your functions accept promise, not just normal values, to make your code look cleaner and closer to working like synchronized code. That's one of the beauties of promise. On Jedi Toolkit's blog, from a slightly different point of view, there is another post about this concept.

For smooth internal use of API

Someone commented on my article and said:

I think those of us who wrote about the promise implementation did a terrible job of explaining what promise does. My point is that you, as a user, should never be forced to interact with promise using then (). Then () is a way for promise consumer libraries to interact with each other and provide smooth API. You should still use callbacks and events as usual.

His comments fit well with the previous section on the use of promise to represent numerical values. By using promise to represent numeric values, we can create a simple API like the one seen above, but here we are talking about a linked API. Basically, this commentator is telling us to use callbacks at the end of an asynchronous chain of commands so that we are still doing what we used to do (using callbacks at the end), and no one can say we are using promise. He wants to keep promise away from ordinary users and only use it internally in our own library. Take a look at this query database and more and more examples of asynchronously filtering results.

Chained .then Calls

Database.find () .then (function (results1) {return results1.filter ('name',' Joe');}) .then (function (results2) {return results2.limit (5);}) .then (function (resultsForReal) {/ / Do stuff with the results})

Whatever the reason, filter and limit are actually asynchronous. Maybe you don't think they should, but that's what they are. Well, critics suggest modifying API to ensure that users can use it like this:

A smooth example of API

Database.find (). Filter ('name',' Joe'). Limit (5, function (results) {/ / Do stuff with the results})

It seems more interesting to me. If you can control its operation, this is the route you should take. You can change it a little bit to replace the normal callback and still return promise:

Return to Promise

Var results = database.find () .filter ('name',' Joe'). Limit (5); results.then (function (realResults) {/ / Do stuff with the results}); / / OR use results like in the previous section: doStuffWith (results)

The choice is yours. I think seasoned developers understand that there's nothing wrong with returning promise to users, but it's a matter of opinion. Either way, it's much better than if we need to string it up and then call it.

Error isolation of synchronous parallelism

There is a quite famous article called You're Missing the Point of Promises, which is consistent with this article. In that article, Domenic Denicola (a good name for alliteration) guided part of the Promises/A specification for then functions.

Then (fulfilledHandler, errorHandler, progressHandler)

FulfilledHandler, errorHandler, and progressHandler are added, and these three handlers are called when the promise is complete. When the Promise is complete, call fulfilledHandler. Call errorHandler when Promise fails. ProgressHandler is called when the postgress event is triggered. All parameters are optional, and values of non-function types are ignored. Not only is the progressHandler parameter optional, but progress is also completely optional. Implementers of Promise do not need to call progressHandler at any time, but call it if a postgress event arrives.

When the given fulfilledHandler and errorHandler callback functions are complete, this function should return a successful new promise. This allows promise operations to be strung together. The return value of the callback function handler is the completed value of the returned promise. If the callback function throws an error, the returned promise is handed over to a failed state.

In his article, he indicated the use in the last paragraph, which he called the "most important" paragraph. He said:

The problem is that promises is not an aggregate of callback functions. It's a simple function. Promises is something deeper, specifically, it provides a direct correspondence between synchronous and asynchronous functions.

I totally agree with this view. Then he continued to pay special attention to the last sentence: "if the callback function throws an error, the returned promise will turn to a failed state." The reason he pays attention to this sentence is that the implementation of jQuery may have implemented the Promises/A specification, but did not do so. If an error is thrown in the callback function, it becomes untrapable in the implementation of promise.

This is very important for most people, although I haven't encountered this situation yet, which is indeed a problem because I don't often throw mistakes. The promise of an error is equivalent to an error or exception, so if an error exists, it should be a failure instead of throwing an error. In this way, we can continue to use promise for parallel synchronization operations. I think we should all report this bug to jQuery. The more people report this error, the more likely it will be fixed quickly. JQuery's promise is one of the most commonly used implementations, so we should make sure that they are doing the right thing.

In any case, the point of promise is that it represents the final result value returned by an operation, but the reason for using them is to make synchronization operations more parallel. Since asynchronous programming entered this scenario, there have been pop-up callbacks everywhere, obscuring our code in strange ways. Promise is a way to change it. Promise allows us to write code synchronously while giving us asynchronous execution of the code.

The answer to what is the focus of Promises is shared here. I hope the above content can be of some help to you. If you still have a lot of doubts to be solved, you can follow the industry information channel to learn more about it.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report