In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly explains "what is the principle of Promise in javascript". Interested friends may wish to have a look at it. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn "what is the principle of Promise in javascript"?
Introduction
In order to make it easier for you to understand, let's start with a scene and let everyone follow their thoughts step by step. I'm sure it will be easier for you to understand.
Consider the following request processing to get the user's id
/ example 1 function getUserId () {return new Promise (function (resolve) {/ / Asynchronous request http.get (url, function (results) {resolve (results.id)})} getUserId () .then (function (id) {/ / some processing})
The getUserId method returns a promise that can be registered with its then method (note that the word is registered) the callback that is executed when the promise asynchronous operation succeeds. This way of execution makes asynchronous calls very convenient.
Principle analysis
So how do you implement Promise with functions like this? In fact, according to the above sentence, to achieve a most basic prototype is still very easy.
Minimalist promise prototype
Function Promise (fn) {var value = null, callbacks = []; / / callbacks is an array, because there may be many callbacks this.then = function (onFulfilled) {callbacks.push (onFulfilled);}; function resolve (value) {callbacks.forEach (function (callback) {callback (value);}) } fn (resolve);}
The above code is simple, and the general logic is as follows:
Call the then method to put the callback you want to perform when the Promise asynchronous operation is successful into the callbacks queue, which is actually registering the callback function, and you can think in the direction of observer mode.
The function passed in when the Promise instance is created is assigned a function type parameter, namely resolve, which receives a parameter value, which represents the result returned by the asynchronous operation. When the one-step operation is executed successfully, the user will call the resolve method. In fact, the real operation is to execute the callbacks in the callbacks queue one by one.
In combination with the code in example 1, first, when new Promise, the function passed to promise sends an asynchronous request, then calls the then property of the promise object to register the callback function for which the request is successful, and then when the asynchronous request is successfully sent, the resolve (results.id) method is called, which executes the callback array registered by the then method.
People who believe carefully should be able to see that the then method should be able to be chained, but the most basic and simple version above obviously does not support chained calls. If you want the then method to support chained calls, it is actually very simple:
This.then = function (onFulfilled) {callbacks.push (onFulfilled); return this;}
See? A chained call similar to the following can be achieved with a simple sentence:
/ / example 2 getUserId () .then (function (id) {/ / some processing}) .then (function (id) {/ / some processing})
Add delay mechanism
Careful students should find that there may be another problem with the above code: what if the resolve function is executed before the then method registers the callback? For example, the function inside promise is the synchronization function:
/ / example 3 function getUserId () {return new Promise (function (resolve) {resolve (9876);});} getUserId () .then (function (id) {/ / some processing})
This is obviously not allowed, and the Promises/A+ specification explicitly requires callbacks to be executed asynchronously to ensure a consistent and reliable order of execution. So we need to add some processing to ensure that the then method has registered all callbacks before resolve executes. We can modify the resolve function like this:
Function resolve (value) {setTimeout (function () {callbacks.forEach (function (callback) {callback (value);});}, 0)}
The idea of the above code is also very simple, that is, through the setTimeout mechanism, the logic of performing the callback in resolve is placed at the end of the JS task queue to ensure that the callback function of the then method has been registered when the resolve is executed.
However, there seems to be a problem: if the Promise asynchronous operation is successful, the callback registered before the success of the asynchronous operation will be executed, but the then registered callback called after the success of the Promise asynchronous operation will never be executed again, which is obviously not what we want.
Join statu
Well, in order to solve the problem thrown in the previous section, we have to add the state mechanism, which is known as pending, fulfilled, and rejected.
2.1Promise States in the Promises/A+ specification makes it clear that pending can be converted to fulfilled or rejected and can only be converted once, that is, if pending is converted to fulfilled state, then it can no longer be converted to rejected. And fulfilled and rejected states can only be transformed from pending, and they cannot be converted to each other. A picture is worth a thousand words:
The improved code looks like this:
Function Promise (fn) {var state = 'pending', value = null, callbacks = []; this.then = function (onFulfilled) {if (state =' pending') {callbacks.push (onFulfilled); return this;} onFulfilled (value); return this;} Function resolve (newValue) {value = newValue; state = 'fulfilled'; setTimeout (function () {callbacks.forEach (function (callback) {callback (value);}, 0);} fn (resolve);}
The idea of the above code is this: when resolve executes, the state is set to fulfilled, after which the new callback added by then is called and executed immediately.
There is no place to set state to rejected, and there will be a section devoted to this issue in order to focus on the core code.
Chain Promise
So here comes the problem again: what if the user still registers a Promise in the then function? For example, example 4 below:
/ example 4 getUserId () .then (getUserJobById) .then (function (job) {/ / a pair of job processing}); function getUserJobById (id) {return new Promise (function (resolve) {http.get (baseUrl + id, function (job) {resolve (job);}
I believe that people who have used promise will know that there will be many, so this is the so-called chained Promise.
Chained Promise means that after the current promise reaches the fulfilled state, the next promise (next neighbor promise) starts. So how do we connect the current promise with the next neighbor promise? This is the difficulty here.
In fact, it is not so difficult, as long as return a promise in the then method. That's what 2.2.7 in the Promises/A+ specification says (smiley face) ~
Let's take a look at the hidden then method and resolve method modification code:
Function Promise (fn) {var state = 'pending', value = null, callbacks = []; this.then = function (onFulfilled) {return new Promise (function (resolve) {handle ({onFulfilled: onFulfilled | | null, resolve: resolve});});} Function handle (callback) {if (state = = 'pending') {callbacks.push (callback); return;} / / if nothing is passed in the then if (! callback.onResolved) {callback.resolve (value); return } var ret = callback.onFulfilled (value); callback.resolve (ret);} function resolve (newValue) {if (newValue & & (typeof newValue = 'object' | | typeof newValue = =' function')) {var then = newValue.then; if (typeof then = = 'function') {then.call (newValue, resolve) Return;}} state = 'fulfilled'; value = newValue; setTimeout (function () {callbacks.forEach (function (callback) {handle (callback);});}, 0);} fn (resolve);}
Let's analyze the above code logic with the code of example 4. To make it easier to read, I will post the code of example 4 here:
/ example 4 getUserId () .then (getUserJobById) .then (function (job) {/ / a pair of job processing}); function getUserJobById (id) {return new Promise (function (resolve) {http.get (baseUrl + id, function (job) {resolve (job);}
In the then method, a new Promise instance is created and returned, which is the basis of serial Promise and supports chained calls.
The handle method is a method within promise. The parameter onFulfilled passed by the then method and the resolve passed when creating a new Promise instance are all push to the callbacks queue of the current promise, which is the key to connecting the current promise with the next neighbor promise (the role of handle must be well analyzed here).
The asynchronous operation of promise (getUserId promise) generated by getUserId is successful. Execute its internal method resolve. The parameter passed in is the result of the asynchronous operation id.
Call the handle method to handle the callback in the callbacks queue: the getUserJobById method to generate a new promise (getUserJobById promise)
Executes the resolve method of the new promise (called bridge promise) that was previously generated by the then method of getUserId promise, with an argument of getUserJobById promise. In this case, the resolve method is passed into the then method of getUserJobById promise and returned directly.
When the getUserJobById promise asynchronous operation succeeds, execute the callback in its callbacks: the resolve method in getUserId bridge promise
* execute the callback in the callbacks of promise, the next neighbor of getUserId bridge promise.
More bluntly, you can look at the following picture, one picture is worth a thousand words (all based on your own understanding, welcome to correct if there is anything wrong):
Failure handling
When an asynchronous operation fails, mark its status as rejected and perform a failed callback for registration:
/ example 5 function getUserId () {return new Promise (function (resolve) {/ / Asynchronous request http.get (url, function (error, results) {if (error) {reject (error)) } resolve (results.id)})} getUserId () .then (function (id) {/ / some processing}, function (error) {console.log (error)})
With previous experience with fulfilled status, it is easy to support error handling by adding new logic to registering callbacks and handling state changes:
Function Promise (fn) {var state = 'pending', value = null, callbacks = [] This.then = function (onFulfilled, onRejected) {return new Promise (function (resolve, reject) {handle ({onFulfilled: onFulfilled | | null, onRejected: onRejected | | null, resolve: resolve, reject: reject});});} Function handle (callback) {if (state = = 'pending') {callbacks.push (callback); return;} var cb = state =' fulfilled'? Callback.onFulfilled: callback.onRejected, ret; if (cb = null) {cb = state = = 'fulfilled'? Callback.resolve: callback.reject; cb (value); return;} ret = cb (value); callback.resolve (ret);} function resolve (newValue) {if (newValue & & (typeof newValue = 'object' | | typeof newValue = =' function')) {var then = newValue.then If (typeof then = = 'function') {then.call (newValue, resolve, reject); return;}} state =' fulfilled'; value = newValue; execute ();} function reject (reason) {state = 'rejected'; value = reason Execute ();} function execute () {setTimeout (function () {callbacks.forEach (function (callback) {handle (callback);});}, 0);} fn (resolve, reject);}
The above code adds a new reject method to be called when an asynchronous operation fails, and extracts the common parts of resolve and reject to form the execute method.
Error bubbling is a very useful feature that has been supported by the above code. When no callback for failed asynchronous operation is specified in handle, the bridge promise (the promise returned by the then function, which is the same later) will be set to rejected status directly, thus achieving the effect of performing subsequent failed callbacks. This helps to simplify the cost of handling failures in serial Promise, because a set of asynchronous operations tend to correspond to an actual function, and failure handling is usually consistent:
/ example 6 getUserId () .then (getUserJobById) .then (function (job) {/ / handle job}, function (error) {/ / getUserId or getUerJobById error console.log (error);})
Exception handling
Careful students will think: what if the code goes wrong in the era of successful callback and failure callback? For such exceptions, you can use try-catch to catch errors and set bridge promise to the rejected state. The handle method is modified as follows:
Function handle (callback) {if (state = = 'pending') {callbacks.push (callback); return;} var cb = state =' fulfilled'? Callback.onFulfilled: callback.onRejected, ret; if (cb = null) {cb = state = = 'fulfilled'? Callback.resolve: callback.reject; cb (value); return;} try {ret = cb (value); callback.resolve (ret);} catch (e) {callback.reject (e);}}
If in an asynchronous operation, executing resolve or reject multiple times will repeatedly handle subsequent callbacks, it can be solved by building a flag bit.
At this point, I believe you have a deeper understanding of "what is the principle of Promise in javascript". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.