The promise of deferred in JQ asynchronous programming

Source: Internet
Author: User
Tags abstract constructor getscript

The logic behind an asynchronous operation is performed by a callback function, which, as the name implies, is called a "callback function", the function body is defined beforehand, and at some point in the future it is triggered by an event that is not controlled by the program itself.

Give some common examples:

Event Bindings


Animation


Ajax


The above examples are simple, typical, easy to read and understand.

To elicit the topic of this article, assume that there are now 3 Ajax asynchronous operations, a, B, C, each encapsulated as a function, and can pass in the success callback as an argument.

Consider the following scenario:

It is hoped that these 3 asynchronous operations will execute sequentially, forming the execution queue, that is, a executed, b execution; b completed, c implementation;

Hope A,b finished, then execute C;

For a scenario (1), the code would probably be like this:


According to the traditional way of writing, will naturally form a callback function of multi-layer nesting, if the amount of code is more, the readability and maintenance of the code will be relatively poor.

For scenarios (2), the code may be more complex:


A A, B operations need to have a state variable isadone,isbdone, the default value is false; A successful isadone after the success of the True,b Isbdone set to true;

b requires a piece of code to repeatedly detect the above 2 state variables, we call this behavior pending, and so pending to 2 states are true to execute C, and terminate pending;

As far as this scenario is concerned, the above scenario can be reluctantly accepted, and if the situation is more complex, the readability and maintainability of the code will be greatly compromised.

The above 2 scenarios, only limited the execution of 3 asynchronous operations, the actual development scenario may be more complex than this, in order to enable developers to more gracefully handle JS asynchronous operation, JQ introduced the Deferred object (1.Deferred), we first to understand the Deferred object, and see what benefits it can bring to our asynchronous programming.


Introduction to deferred characteristics

Create a deferred instance first.


The new keyword is optional and can not be written, because it is handled in the deferred constructor, but does not mean that the object is created with native JS based on the constructor without writing new.

Deferred has 3 states: Pending (Pending), Resolved (success), Rejected (failure).

The deferred state can be toggled through the API, but not reversed.


Can be understood from the English literal meaning: Resolve (solve) after success, reject (rejection) after the failure.

State irreversibility means that once you switch from a pending state to any of the determined states, calling resolve or reject again will have no effect on the original state.

Deferred the callback functions that are executed when different states are bound by the semantics-corresponding APIs.


Resolve:done, always

Reject:fail, always

Where the always-bound callback function, regardless of whether the deferred state succeeds or fails, is always performed.

Deferred also has a then method to simplify the written, fail writing:


Also, it is noteworthy that when the deferred state is switched (after invoking reject or resolve) and then the callback function is bound, then the callback function for the corresponding state is executed immediately.

As an example:


Deferred can also return its own subset of operations.


Promise only the API that binds the callback, not the state-switched APIs.

Deferred:reject, resolve, done, fail, then, always

Promise:done, fail, then, always

Obviously promise is used to open to external calls, and deferred is often used inside the module to control the transitions of its state.

To give an intuitive example, generally in JS we will use settimeout to do delay, such as:


We use deferred to encapsulate the code as follows:


Inside the wait function, the deferred is used to toggle the state, returning the Promise object so that the callback function can be bound outside the wait.

In fact, the principle in JQ has a very typical case, that is the $.ajax method, the method will return a promise, and the method inside a deferred to determine their own state, so the relative traditional Ajax writing, we can write this:


Methods such as Done,fail can also accept multiple callback, or callback array as parameters.

Assuming that there is an AJAX request, the success of the return result requires 3 separate processing, corresponding to 3 functions, we can write:


Thus the logic of acquiring data is separated from the logic of processing data, and data processing is not all stacked in the success callback function, and the code as a whole looks more concise and readable.


Back to the previous question

After understanding the deferred features and simple applications, we look back at the 2 scenarios mentioned above, can we use a better solution? The answer is yes.

Let's look at the scene first (2): A, B complete, and then execute c.

Why look at the scene first (2), because there is a ready-made api:$.when in the JQ, it is convenient to meet the demand.

$.when can accept multiple deferred (promise) objects, $.when returns a promise to be used as a binding for subsequent callbacks, as shown in the official website example:


A MyFunc callback is executed when 2 asynchronous requests in $.when return to success;

A myfailure callback is executed when an asynchronous request fails in the $.when;

The solution has been very clear, we slightly modified before the Asynca, ASYNCB method, let them respectively return to their respective promise, and then directly use $.when can.

I can use Ajax to do the code example first:


Here's a code instance that uses deferred directly:


Overall, do you feel the code clearer and more conducive to understanding?

Let's look at the scene again (1): A finished, b implementation; b finished, c execution.

To complete the transformation of this implementation, we need to understand the then method of the deferred object in depth. Above we introduce the general usage of then:


A simplified formulation used for done,fail. But it also has a very important role that can be used to pass deferred (promise) objects and implement the "Task Chain" (Chain tasks).

Here's how to implement the scenario (1) with then:


is not feeling very simple, if there is D behind C, then continue to go down then on it.


Suppose Asynca is an AJAX operation in which the callback function data parameter, which is the Ajax success, is returned in the background.

It is noteworthy that if the deferred object is passed by the then method, the callback function must return a deferred.

The above example also has one characteristic: because the first parameter of then is the callback that executes when deferred object succeeds, if deferred state switches to fail, then the successful callback of subsequent then will no longer execute, "task Chain" is interrupted.

The scene has a certain practical value, for example, a business site, the page has a number of display modules are Ajax to ask the background to get the data, if the page comes in, while sending several AJAX requests to the background, will instantly increase the background IO pressure, may increase the user waiting for the interface feedback data time, resulting in a decline in experience. In this case, the AJAX request as a queue processing is more appropriate, can be progressively requested to obtain data, improve performance and rendering experience.


Summary

This article is only a preliminary introduction of the next JQ in the deferred, and not in-depth to every detail, but its basic skills should be covered, interested students can go to JQ official website, the study.

In fact, promise can literally be interpreted as "commitment", you can think of deferred as one of your men, you let him follow up on a matter, and when this thing can have a result, you are not clear, but he will give you a promise, will follow your intention: If things like this, follow-up to do what; If that's the case, follow up on what to do.


Creating responsive applications using JQuery Deferred and Promise

In abstraction, Deferreds can be understood as a way of representing time-consuming operations that take a long time to complete, rather than blocking the application waiting for it to complete and then returning the result. The deferred object returns immediately, and then you can bind the callback function to the deferred object, which is invoked after the asynchronous process completes.




Promise





You may have read some information about promise and deferreds implementation details. In this section, we outline how the promise works, which are applicable in almost all JavaScript frameworks that support Deferreds.





In general, promise as a model provides a solution to describe the concept of delay (or future) in software engineering. The idea behind it is that instead of executing a method and blocking the application waiting for the result to return, it returns a Promise object to meet future values.





An example would help to understand that if you are building a Web application, it relies heavily on data from Third-party APIs. Then there is a common problem: we cannot learn the latency of an API response, and other parts of the application may be blocked until it returns results. Deferreds provides a better solution to the problem, which is non-blocking and completely decoupled from the code.





PROMISE/A proposed ' defines a ' then ' method to register the callback, which is executed when the processing function returns the result. It returns a promise pseudo code that looks like this:





Promise = Calltoapi (Arg1, arg2, ...);


Promise.then (function (FutureValue) {


/* Handle FutureValue * *


});


Promise.then (function (FutureValue) {


/* Do something else * *


});





In addition, the promise callback is executed in the following two different states:





Resolved: In this case, the data is available


Rejected: In this case, an error occurred and no values were available





Fortunately, the ' then ' method accepts two parameters: one for promise to be resolved (resolved) and the other for promise rejection (rejected). Let's go back to pseudocode:





Promise.then (function (FutureValue) {


/* We got a value * *


}, function () {


/* Something went wrong * *


} );





In some cases, we need to get multiple return results, and then continue executing the application (for example, a set of dynamic options before the user can select the option they are interested in). In this case, the ' when ' method can be used to solve the scenario where all the promise are satisfied before they can continue.





When


PROMISE1,


Promise2,


...


). Then (function (futureValue1, futureValue2, ...) {


* All promises have completed and are resolved * *


});





A good example of this is a scenario where you may have multiple animations running at the same time. If you do not track callbacks after each animation completes, it is difficult to do the next task after the animation completes. However, using the promise and ' when ' approach can be quite straightforward: once the animation is done, you can perform the next task. The end result is that we can simply use a callback to solve the problem of waiting for multiple animation execution results. For example:





When (function () {


/* Animation 1 * *


/* Return Promise 1 * *


}, function () {


/* Animation 2 * *


/* Return Promise 2 * *


}). Then (function () {


/* Once both animations have completed we can then run our additional logic * *


});





This means that you can basically write code in a non-blocking logical way and execute it asynchronously. Rather than passing the callback directly to the function, this may result in a tightly coupled interface that can easily distinguish between synchronous and asynchronous concepts through promise patterns.





In the next section, we will look at the deferreds of the jquery implementation, and you may find that it is significantly simpler than the promise pattern now seen.




the deferreds of jquery





jquery introduced deferreds for the first time in version 1.5. The method it implements is not much different from the abstract concept we described earlier. In principle, you get the ability to get a ' deferred ' return value at some point in the future. It cannot be used alone until then. Deferreds is added as part of a larger rewrite of the Ajax module, which follows the promise/a design of the COMMONJS. The 1.5 and previous versions contain the deferred feature, which enables $.ajax () to receive callbacks that call completion and request errors, but with serious coupling. Developers typically use other libraries or toolkits to handle deferred tasks.  The new version of jquery provides some enhanced ways to manage callbacks, providing a more flexible way to build callbacks without having to worry about whether the original callback was triggered. It is also noteworthy that the recursive object of jquery supports multiple callbacks to bind multiple tasks, and the task itself can be either synchronous or asynchronous.





You can explore the deferred features in the following table to help you understand which features you need:








Jquery.deferred () Creates a new Deferred object constructor that can take an optional function argument that is invoked after the construction completes.


Jquery.when () This way to perform a callback function on one or more objects that represent an asynchronous task


Jquery.ajax () executes an asynchronous AJAX request that returns the JQXHR object that implements the Promise interface


Deferred.then (Resolvecallback,rejectcallback) adds a callback that the deferred object is resolved or rejected when the handler is invoked.


Deferred.done ()





Call a function or array function when the delay succeeds.


Deferred.fail ()





Call a function or array function when the delay fails ...


Deferred.resolve (ARG1,ARG2, ...) ) calls the ' done ' callback function registered by the deferred object and passes the arguments


Deferred.resolvewith (Context,args) invokes the ' done ' callback function that deferred object registers and passes parameters and sets the callback context


Deferred.isresolved determines whether a deferred object has been resolved.


Deferred.reject (ARG1,ARG2, ...) ) calls the ' fail ' callback function registered by the deferred object and passes the arguments


Deferred.rejectwith (Context,args) invokes the ' fail ' callback function registered by the deferred object and passes the arguments and sets the callback context


Deferred.promise () Returns the Promise object, which is a forged deferred object: It is based on deferred and cannot change state so it can be passed safely











The core of the jquery latency implementation is jquery.deferred: a constructor that can be chained to the call. ...... Note that the default state of any deferred object is unresolved, and the callback is added to the queue through the. then () or. Fail () method, and is executed later in the process.





Here's an example of a $.when () that accepts multiple parameters





function Successfunc () {Console.log ("success!");}


function Failurefunc () {Console.log ("failure!");}





$.when (


$.ajax ("/main.php"),


$.ajax ("/modules.php"),


$.ajax ("/lists.php")


). Then (Successfunc, Failurefunc);





Interestingly in the implementation of $.when (), it is not only possible to parse deferred objects, but also to pass parameters that are not deferred objects, and treat them as deferred objects and immediately execute callbacks (donecallbacks). This is also worth mentioning in the deferred implementation of jquery, in addition, Deferred.then () also provides support for the Deferred.done and Deferred.fail () methods to increase callbacks in deferred queues.





With the deferred features mentioned in the table described earlier, let's look at a code example.  Here we create a very basic application: get an external news source (1) by $.get method (return a Promise) and (2) get the latest reply. The program also implements the animation of the news and reply content display container through functions (Prepareinterface ()).





To ensure that the above three steps are complete before performing other related actions, we use $.when (). Depending on your needs. then () and. Fail () processing functions can be used to perform other program logic.





function Getlatestnews () {


Return $.get ("latestnews.php", function (data) {


Console.log ("News data received");


$ (". News"). HTML (data);


} );


}


function Getlatestreactions () {


Return $.get ("latestreactions.php", function (data) {


Console.log ("Reactions data received");


$ (". Reactions"). HTML (data);


} );


}





function Prepareinterface () {


Return $. Deferred (function (DFD) {


var latest = $ (". News,. Reactions");


Latest.slidedown (Dfd.resolve);


Latest.addclass ("active");


}). Promise ();


}





$.when (


Getlatestnews (), Getlatestreactions (), Prepareinterface ()


). Then (function () {


Console.log ("Fire after Requests succeed");


}). Fail (function () {


Console.log ("Something went wrong!");


});





The use of deferreds in the behind-the-scenes operation of Ajax does not mean that they cannot be used elsewhere. In this section, we'll see that in some solutions, using deferreds will help abstract out asynchronous behavior and decouple our code.




Asynchronous Caching





When it comes to asynchronous tasks, caching can be a bit harsh because you have to make sure that the same key task is executed only once. Therefore, your code needs to track inbound tasks in some way. For example, the following code fragment:





$.cachedgetscript (URL, callback1);


$.cachedgetscript (URL, callback2);





Caching mechanisms need to ensure that scripts are only requested once, regardless of whether they already exist in the cache. Therefore, in order for the caching system to handle the request correctly, we eventually need to write some logic to track the callbacks that are bound to the given URL.





Thankfully, this happens to be the kind of logic that deferred implements, so we can do this:





var cachedscriptpromises = {};


$.cachedgetscript = function (URL, callback) {


if (!cachedscriptpromises[url]) {


cachedscriptpromises[URL] = $. Deferred (function (defer) {


$.getscript (URL). Then (Defer.resolve, Defer.reject);


}). Promise ();


}


return cachedscriptpromises[URL].done (callback);


};





The code is fairly simple: we cache a Promise object for each URL. If the given URL does not promise, we create a deferred and issue the request. If it already exists we just need to bind the callback for it. One of the great advantages of the solution is that it transparently handles new and cached requests. Another advantage is that a deferred cache gracefully handles failures. When promise ends with a ' rejected ' state, we can provide an error callback to test:





$.cachedgetscript (URL). Then (Successcallback, Errorcallback);





Keep in mind that the code snippet above will work regardless of whether the request is cached or not!




Universal Asynchronous Caching





To make the code as generic as possible, we set up a cache factory and abstract the tasks that actually need to be performed:





$.createcache = function (requestfunction) {


var cache = {};


return function (key, callback) {


if (!cache[key]) {


cache[key] = $. Deferred (function (defer) {


Requestfunction (defer, key);


}). Promise ();


}


Return cache[key].done (callback);


};


}





Now that the specific request logic has been abstracted, we can write back the cachedgetscript:





$.cachedgetscript = $.createcache (function (defer, URL) {


$.getscript (URL). Then (Defer.resolve, Defer.reject);


});





Each time you call Createcache, a new cache library is created and a new cached retrieval function is returned. Now we have a common cache factory that can easily implement a logical scenario that involves taking a value from the cache.




Picture Loading





Another candidate scenario is image loading: Make sure we don't load the same image two times, we may need to load the image. Using Createcache is easy to achieve:





$.loadimage = $.createcache (function (defer, URL) {


var image = new Image ();


function CleanUp () {


Image.onload = Image.onerror = null;


}


Defer.then (CleanUp, cleanUp);


Image.onload = function () {


Defer.resolve (URL);


};


Image.onerror = Defer.reject;


image.src = URL;


});





The next code snippet is as follows:





$.loadimage ("My-image.png"). Done (CALLBACK1);


$.loadimage ("My-image.png"). Done (Callback2);





The cache works regardless of whether the image.png has been loaded or is in the process of loading.




API response to cached data





Which of your pages is considered immutable in the lifecycle of the API request, is also the cache perfect candidate scenario. For example, perform the following actions:





$.searchtwitter = $.createcache (function (defer, query) {


$.ajax ({


URL: "Http://search.twitter.com/search.json",


Data: {Q:query},


DataType: "Jsonp",


Success:defer.resolve,


Error:defer.reject


});


});





The program allows you to search on Twitter while caching them:





$.searchtwitter ("JQuery Deferred", callback1);


$.searchtwitter ("JQuery Deferred", Callback2);





timed





Deferred based caching is not limited to network requests; it can also be used for timed purposes.





For example, you might want to perform an action on a Web page for a period of time, to attract users ' attention to a particular feature that is not easily noticed, or to deal with a latency problem. Although settimeout is suitable for most use cases, the solution cannot be provided after the timer is set off and even theoretically expires. We can use the following caching system to handle:





var readytime;


$ (function () {readytime = Jquery.now ();});


$.afterdomready = $.createcache (function (defer, delay) {


Delay = Delay | | 0;


$ (function () {


var delta = $.now ()-readytime;


if (Delta >= delay) {defer.resolve ();}


else {


SetTimeout (Defer.resolve, Delay-delta);


}


});


});





The new Afterdomready helper method provides the right time after domready with the fewest counters. If the delay has expired, the callback will be executed immediately.




Synchronizing multiple animations





Animations are another common example of asynchronous tasks. However, it is still a bit challenging to execute code after several unrelated animations have been completed. Although the ability to get promise objects on an animated element is provided in jQuery1.6, it is easy to manually implement:





$.fn.animatepromise = function (prop, speed, easing, callback) {


var elements = this;


Return $. Deferred (function (defer) {


Elements.animate (prop, speed, easing, function () {


Defer.resolve ();


if (callback) {


Callback.apply (this, arguments);


}


});


}). Promise ();


};





Then, we can synchronize different animations using $.when ():





var fadediv1out = $ ("#div1"). Animatepromise ({opacity:0}),


fadediv2in = $ ("#div1"). Animatepromise ({opacity:1}, "fast");





$.when (Fadediv1out, fadediv2in). Done (function () {


/* Both animations ended * *


});





We can also use the same techniques to create a number of helper methods:





$.each (["Slidedown", "Slideup", "Slidetoggle", "FadeIn", "fadeout", "Fadetoggle"],


function (_, name) {


$.fn[name + "Promise"] = function (speed, easing, callback) {


var elements = this;


Return $. Deferred (function (defer) {


elements[name] (speed, easing, function () {


Defer.resolve ();


if (callback) {


Callback.apply (this, arguments);


}


});


}). Promise ();


};


});





Then you can use the new helper code to synchronize the animation as follows:





$.when (


$ ("#div1"). Fadeoutpromise (),


$ ("#div2"). Fadeinpromise ("Fast")


). Done (function () {


/* Both animations are done * *


});








One-off event





While jquery provides all the time-binding methods you might need, the situation can become a bit tricky when the event needs to be handled only once. (unlike $.one ())





For example, you might want to have a button that opens a panel when it is first clicked, and then executes a specific initialization logic after the panel is opened. When dealing with this situation, people usually write code like this:





var buttonclicked = false;


$ ("#myButton"). Click (function () {


if (!buttonclicked) {


ButtonClicked = true;


Initializedata ();


Showpanel ();


}


});





After a while, you may be able to add some action when you click on the button after the panel is open, as follows:





if (buttonclicked) {/* Perform specific action */}





This is a very coupled solution. If you want to add some other action, you must edit the binding code or copy one. If you don't, your only option is to test buttonclicked. Since buttonclicked may be false, the new code may never be executed, so you may lose the new action.





We can do better with deferreds (for simplicity, the following code applies only to a single element and a single event type, but it can easily be extended to a collection of multiple event types):





$.fn.bindonce = function (event, callback) {


var element = $ (this[0]),


defer = Element.data ("Bind_once_defer_" + event);


if (!defer) {


Defer = $. Deferred ();


function Defercallback () {


Element.unbind (event, Defercallback);


Defer.resolvewith (this, arguments);


}


Element.bind (event, Defercallback)


Element.data ("Bind_once_defer_" + event, defer);


}


Return Defer.done (callback). Promise ();


};





The code works as follows:





Checks whether the element is already bound to a deferred object for a given event


If not, create it so that it resolves at the first time that the event is triggered


Then bind the given callback to the deferred and return promise





Although the code is verbose, it simplifies the handling of related issues. Let's first define an auxiliary method:





$.fn.firstclick = function (callback) {


Return This.bindonce ("click", Callback);


};





The previous logic can then be refactored as follows:





var Openpanel = $ ("#myButton"). Firstclick ();


Openpanel.done (initializedata);


Openpanel.done (Showpanel);





If we need to do some action, only when the panel is open, all we need is this:





Openpanel.done (function () {/* Perform specific action */});





If the panel is not open, the action is delayed until the button is clicked.




Portfolio Assistant





Looking at each of these examples alone, promise's role is limited. However, the real power of promise is to mix them together.











Load Panel contents and open panel on first click





If we have a button, we can open a panel, request its contents, and fade into the content. Using the helper method we defined earlier, we can do this:





var panel = $ ("#myPanel");


Panel.firstclick (function () {


$.when (


$.get ("panel.html"),


Panel.slidedownpromise ()


). Done (function (ajaxresponse) {


Panel.html (ajaxresponse[0]). FadeIn ();


});


});





load the image and open the Panel on the first click





If we already have content on the panel, we only want to load the image the first time the button is clicked and fade in the image when all the images are loaded successfully. The HTML code is as follows:





<div id= "Mypanel" >


<img data-src= "Image1.png"/>


<img data-src= "Image2.png"/>


<img data-src= "Image3.png"/>


<img data-src= "Image4.png"/>


</div>





We use the Data-src property to describe the true path of the picture. The code that uses the Promise helper to resolve the use case is as follows:





$ ("#myButton"). Firstclick (function () {


var panel = $ ("#myPanel"),


promises = [];


$ ("img", panel). each (function () {


var image = $ (this), src = element.attr ("data-src");


if (SRC) {


Promises.push (


$.loadimage (SRC). Then (function () {


Image.attr ("src", SRC);


}, function () {


Image.attr ("src", "error.png");


} )


);


}


});





Promises.push (Panel.slidedownpromise ());





$.when.apply (null, promises). Done (function () {Panel.fadein ();});


});





The trick here is to track all the LoadImage promise, and then add the panel Slidedown animation. So the first time you click the button, the panel will slidedown and the image will start to load. The panel fades in as soon as you complete the slide down panel and all the images that have been loaded.




load the image on the page after a specific delay





Suppose we want to implement a deferred image display throughout the page. To do this, the HTML format we need is as follows:





<img data-src= "Image1.png" data-after= "1000" src= "Placeholder.png"/>


<img data-src= "Image2.png" data-after= "1000" src= "Placeholder.png"/>


<img data-src= "Image1.png" src= "Placeholder.png"/>


<img data-src= "Image2.png" data-after= "src=" placeholder.png





The meaning is very simple:





Image1.png, the third image is immediately displayed, one second after the first image display


Image2.png second image after one second, two seconds to display fourth image





How will we achieve it?





$ ("img"). each (function () {


var element = $ (this),


src = element.attr ("Data-src"),


after = Element.attr ("Data-after");


if (SRC) {


$.when (


$.loadimage (SRC),


$.afterdomready (after)


). Then (function () {


Element.attr ("src", SRC);


}, function () {


Element.attr ("src", "error.png");


}). Done (function () {


Element.fadein ();


});


}


});





If we want to delay the loading of the image itself, the code will be different:





$ ("img"). each (function () {


var element = $ (this),


src = element.attr ("Data-src"),


after = Element.attr ("Data-after");


if (SRC) {


$.afterdomready (After, function () {


$.loadimage (SRC). Then (function () {


Element.attr ("src", SRC);


}, function () {


Element.attr ("src", "error.png");


}). Done (function () {


Element.fadein ();


});


} );


}


});





Here, we first wait for the delay condition to be satisfied before attempting to load the picture. It makes sense to limit the number of network requests when you want to load a page.


Conclusion

As you can see, promise is useful even in the absence of Ajax requests. By using the deferred implementation in jquery 1.5, it is very easy to isolate asynchronous tasks from your code. In this way, you can easily detach logic from your application.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.