Code in JavaScript is single-threaded, so all network operations and browser events in JavaScript must be executed asynchronously.Before Promise, JavaScript handled asynchronization in a callback function.It can be said that the way callback is already very popular, so what is Promise solving?Look at the following code:
$.get('/getList',function(){ $.get('/getCount',function(){ $.get('/getDetail',function(){ //.... }) }) })
This code is the traditional callback way to handle asynchronism. You can see that just level 3 of nested code level is already a bit messy, and you can't look straight into it if you add some logic code.This is what we often say==Callback to Hell==Problem.Code readability is low, difficult to maintain, and cannot be reused.
Promise Resolves Callback Hell
Promise
Basic usagevar promise = new Promise((resolve,reject)=>{ setTimeout(function(){ //Now that the asynchronous operation is complete, resolve tells the outside world that other operations are possible resolve('ok'); //reject('no'); },2000); }) promise.then(res=>{ console.log(res); // ok },err=>{ console.log(err); // no })
Processing asynchronous through Promise, performing the asynchronous operation first, regardless of how the result is handled, returning success or failure through the Promise object, and executing the result processing function at some point in the future.The code becomes flat and easy to read and maintain.
- resolve && reject
The code above sets Promise's state to Resolved through the resolve method, where the then method catches the change and performs a callback to the success situation.
The reject method sets the Promise state to Rejected, and the then method performs a callback to the Failed condition (the second parameter of the then method).
Promise implements multilevel callbacks
The same three-level callback code we'll use Promise to refactor it again
new Promise((resolve,reject)=>{ $.get('/getList',res=>{ resolve(res); }); }).then(res=>{ return new Promise((resolve,reject)=>{ $.get('/getCount',res=>{ resolve(res); }); }); }).then(res=>{ return new Promise((resolve,reject)=>{ $.get('/getDetail',res=>{ resolve(res); }) }); }).then(res=>{ //... });
You can see that no matter how many callbacks there are, there is no need to nest each other, just wait for the Promise object to "notify" to execute.
Promise.all
Promise.all can be used when multiple asynchronous operations without associated logic are required.
Promise.all([ $.get('/getList'), $.get('/getCount'), $.get('/getDetail') ]).then(([data1,data2,data3])=>{ //The argument to the then callback is an array whose order is the writing order of the asynchronous operation console.log(data1,data2,data3); },err=>{ console.log(err); });
- The parameter in all method should be a Promise object, because the ajax function returns the promise object, so it can be executed here.
- This method is only applicable when there is no logical association between asynchronous operations;
- Regardless of the order in which the asynchronous operations are performed, they are returned in the writing order (data1,data2,data3 is returned in the writing order of the asynchronous operations);
- If one of these errors occurs, an error callback will be executed;
Promise.race
The use of race is similar to all, except that all waits until all asynchronous operations have been executed before performing the then callback, which is executed as soon as one asynchronous operation in race has been executed.
Promise.race([ new Promise(function(resolve, reject){ setTimeout(function(){ console.log('Function 1 executes!'); resolve('11'); }, 1000); }), new Promise(function(resolve, reject){ setTimeout(function(){ console.log('Function 2 executes!'); resolve('22'); }, 1200); }) ]).then(result=>{ console.log('then implement'); console.log(result); },err=>{ console.log(err); }); //The result of execution is: //Function 1 executes! //then execute //11 //Function 2 executes!
You can see that function 1 executes significantly faster than function 2, so the call-back is executed immediately after the function is executed, ==Note that function 2 still executes==, but the call-back execution is not triggered after execution.
Promise Error Handling
- The first way is through then.
new Promise((resolve,reject)=>{ //... }).then(resSuccess=>{ //Success },resError=>{ //fail });
- Second, catch capture
new Promise((resolve,reject)=>{ //... }).then(resSuccess=>{ //Success }).catch(resError=>{ //fail });
==Note==:catch is more common because it captures not only the parameters passed by reject, but also errors that occur in successful callbacks.
(*Deferred object and its methods)
jQuery implements the Promise specification with $.Deferred.
function fn1() { var def = $.Deferred(); //Perform Asynchronous Operation setTimeout(function () { console.log('Function 1!'); def.resolve('Callback after function 1 executes'); //def.reject('Callback after function 1 executes'); }, 1000); return def.promise(); } //The first parameter of the then method receives a successful callback and the second parameter receives a failed callback fn1().then(res => { console.log(res); },err=>{ console.log(err); });
The $.Deferred() method returns an object, which we can call a Deferred object, which contains methods such as then, done, fail, and so on.
jQuery uses this Deferred object to implement the Promise specification.
In the case of multilevel callbacks, the then() method can also be used to make chain calls:
function fn1() { var def = $.Deferred(); //Perform Asynchronous Operation setTimeout(function () { console.log('Execute function 1!'); def.resolve('Callback after function 1 executes'); }, 1000); return def.promise(); }; function fn2() { var def = $.Deferred(); //Perform Asynchronous Operation setTimeout(function () { console.log('Execute function 2!'); def.resolve('Callback after function 2 is executed'); }, 1000); return def.promise(); }; fn1().then(res => { console.log(res); //The core of chainable calls is to return a Deferred object return fn2(); }).then(res => { console.log(res); }); //Execute function 1! //Callback after function 1 executes //Execute function 2! //Callback after function 2 is executed
done() && fail()
done and fail are used similarly to then, but are actually grammatical sugars.
function fn1() { var def = $.Deferred(); setTimeout(function () { console.log('Execute function 1!'); def.resolve('Callback after function 1 executes'); // def.reject('function 1 execution failure callback'); }, 1000); return def.promise(); }; fn1().then(function (res) { console.log(res); }, function (err) { console.log(err); }); fn1().done(function (res) { console.log(res); }).fail(function (err) { console.log(err); });
always()
There is also an always method on the Defferred object that performs an always callback regardless of the result returned by the asynchronous operation.
function fn1() { var def = $.Deferred(); setTimeout(function () { console.log('Execute function 1!'); def.resolve('Callback after function 1 executes'); // def.reject('function 1 execution failure callback'); }, 1000); return def.promise(); }; fn1().then(function (res) { console.log(res); }, function (err) { console.log(err); }).always(() => { console.log('Success or failure will enter here'); });
Due to Promise, jQuery's Deferred object is rarely used.This is not an example of some other usages. If you are interested, you can go to the official website to learn more.