The basic concept
Abstractly, Promise is a solution for asynchronous programming in JavaScript.
Specifically, a Promise is an object that is typically used to describe asynchronous behavior that executes now and gets results some time later, holding the results of that asynchronous behavior internally.
The Promise object has and only three states:
- -sheldon: I’m still pending.
- Success fulfilled:
- Rejected: failure
There are only two kinds of state transition for a Promise. Once the Promise decides, it will always keep its decision (depressing or rejected) unchanged:
- Pending to fulfilled
- Pending to the rejected
Basic usage
The Promise object constructor receives an Executor executor
let p = new Promise((resolve,reject) = > {
// This code block is executor
})
Copy the code
Actuators typically undertake two tasks:
- Initialize an asynchronous behavior
- Control the final transition of state
The executor accepts two functions as arguments, where:
- Resolve: Used to convert state pending to depressing
- Reject: Used to convert the state pending to Rejected
let p = new Promise((resolve,reject) = > {
setTimeout(() = > {
resolve()
},1000)})Copy the code
After setTimeout has actually been executed 1000ms, the state of object P transitions from Pending to depressing, and the corresponding callback function resolve is placed on an asynchronous queue (waiting for execution).
Instance method.then ()
The then method takes two functions as arguments
- OnResolved: When resolve() is executed in executor, onResolved will pass the value that succeeded
- The second is onRejected: When the executor executes reject(), the onRejected function is called and the reason is passed
We can write these two functions outside separately
let p = new Promise((resolve, reject) = > {
console.log('Promise executed successfully')
resolve(3)
})
p.then(onResolved,onRejected)
function onResolved(value){
console.log('resolved:+ value)
}
function onRejected(reason){
console.log('the rejected:+ reason)
}
// Output is as follows:
// Promise executes successfully
/ / resolved: 3
Copy the code
You can also write the function directly inside the then parentheses (omitting the definition of the function name)
let p = new Promise((resolve, reject) = > {
console.log('Promise executed successfully')
resolve(3)
})
p.then((value) = >{
console.log('the rejected:+ value)
}, (reason) = >{
console.log('the rejected:+ reason)
})
// This is exactly the same as above
Copy the code
Arguments to the THEN method are optional
When onResolved is the only parameter you can write:
let p = new Promise((resolve, reject) = > {
resolve(3)
})
p.then((value) = >{
console.log('resolved:+ value)
})
Copy the code
If only onRejected is used, set the first parameter to null
let p = new Promise((resolve, reject) = > {
reject('error')
})
p.then(null.(reason) = >{
console.log('the rejected:+reason)
})
Copy the code
Instance method.catch()
A catch is designed to handle failed promise objects, and it accepts only an onRejected function as an argument
let p = new Promise((resolve, reject) = > {
reject('error')
})
p.catch((reason) = > {
console.log(reason)
})
Copy the code
How do you determine a Promise or something like a Promise
Although Promise is through new Promise(…) Syntax creation, but using p instanceof Promise to check whether a value is a Promise is not comprehensive, because:
- The Promise value may have been received from another browser window (iframe), and the Promise in that window may not be the same as the Promise in the current window, so the Promise instance is not recognized
- Instead of using native ES6 Promises, some library or framework will implement its own Promise
So, identifying a Promise is defining a thenable, anything that has then(…) Method objects and functions are called promise-consistent thenable.
const thenable = {
then(res) {
setTimeout(res, 3000)}}/ / 1
Promise.resolve()
.then(() = >thenable)
.then(() = >console.log('3 seconds gone '));
/ / 2
!async function() {
const sleep = () = > thenable
await sleep();
console.log('3 seconds gone '); } ();Copy the code
As in the above code, no matter which way you write it, it will take 3 seconds and then print. The proof determines whether an object is a Promise or behaves like a Promise by simply determining whether it has a then function.
if( p ! = =null &&
(
typeof p === "object" ||
typeof p === "function"
) &&
typeof p.then === "function"
) {
// This is a thenable object
} else {
// This is not a thenable object
}
Copy the code
If we unintentionally add then to an object but don’t want it to be treated as a Promise or thenable, we’re afraid it will backfire. It will be automatically recognized as Thenable and treated according to certain rules. So this can be harmful and can lead to hard-to-track bugs.
This type detection is called duck typing: When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.
Why use Promise
There are trust issues if you use normal callbacks to provide an asynchronous scheme, as in this code:
// A
ajax('.. '.function(.) {
// C
});
// B
Copy the code
A and B occur now, while C may be deferred to the future and under the control of A third party. There are five problems with this controlled reverse transfer:
- Too few or too many callbacks (third parties may not call the callback as many times as we would like)
- Calling the callback too early (before tracing)
- Calling the callback too late (not even called)
- The required environment/parameters are not passed to the callback function
- Swallow possible errors or exceptions
Promise features are designed to address these issues:
-
Addressing callbacks too early or too late: Even if a promise is completed immediately, the contents of its callback then function are always placed in a microtask queue and executed asynchronously, and even if there are multiple callbacks, they execute independently of each other. Such as:
p.then(function() { p.then(function() { console.log('C'); }); console.log('A'); }) p.then(function() { console.log('B'); }) // A B C Copy the code
-
Solve the problem of too few callbacks (not called) : We can set up a timeout function and use promise.race to solve the problem of not calling timeout
function timeout(delay) { return new Promise((resolve, reject) = > { setTimeout(() = > { console.log('Timeout! '); }); }); } Promise.race([p, timeout(3000)]).then(resCb, rejCb); // When a timeout occurs or p throws an error, rejCb is invoked Copy the code
-
Resolve too many callbacks: If your code tries to call resolve or Reject more than once, the Promise will only accept the first resolution and ignore any subsequent calls.
var p = new Promise((resolve, reject) = > { // ... resolve('1'); // All subsequent resolutions are ignored resolve('2'); reject('3'); }); p.then((value) = > { console.log(value); }, (reason) = > { console.error(reason); }); / / 1 Copy the code
-
Resolve failure to pass parameter/environment values: Promise’s resolve and Reject can pass only one argument, the second and subsequent arguments are ignored and undefined if not explicitly defined, as described in the chained call flow below.
-
Each then function returns another promise, so any error thrown anywhere will result in the corresponding promise being rejected. You can define exception handling in the second argument to catch or then, as described in error handling below.
Reference article:
Understand Promise again
JavaScript You Don’t Know (Middle Volume)