With asynchronous
Writing in the front
Today is a review of asynchrony, at the front end today. Asynchrony is already very important thing, even can say is don’t understand asynchrony, what can’t do
Today we will sum up from several aspects
- Summarize common asynchronous solutions
- Write promise by hand
Asynchrony goes through four stages
Callback functions – > Promise – > Generator – > async/await.
Review some of the questions that were easy to get confused at first
Q: First of all, we all know that JS is a single-threaded language. Is it inconsistent with its single-threaded execution mode that a single thread can open an asynchronous task?
A: JS execution is single-threaded, but browsers are multi-threaded. That is, the asynchrony of JS is realized jointly by the JS execution thread and the browser event trigger thread (combined with the experience of Electron development to understand the multi-thread operation in browsing). It is not easy for processes to communicate with each other, threads do not have this worry. The js thread encounters an asynchronous code and requests assistance from the browser. The browser opens a new thread for processing, and the event listener thread listens for the result of processing
Getting down to business (common asynchronous solution)
1. Callback function
Read chestnut directly (read a text) :
const fs = require("fs");
const path = require("path")
fs.readFile(path.join(__dirname, "./test.txt"), "utf8", (err, data) => {
console.log(data);
})
Copy the code
2. Publish subscriptions
Write a “signal center.” When a “signal” is issued inward after the completion of an asynchronous task, the subscriber’s callback can begin execution (publish-subscribe mode)
class Event {
constructor() {
this.subs = [];
}
on(fn) {
this.subs.push(fn)
}
emit(data) {
this.subs.forEach(fn= >fn(data)); }}const fs = require("fs");
const path = require("path")
let e = new Event();
e.on((val) = > {
console.log(val);
})
fs.readFile(path.join(__dirname, "./test.txt"), "utf8", (err, data) => {
if (err) return;
e.emit(data);
})
Copy the code
Note: Some documentation says publish subscribe is observer, which is not true. Publish subscriptions are more like a subset of observers (expand later when summarizing design patterns)
3. Event monitoring
This thing, I’ve already written the front end
demo.onclick=function(){}
Copy the code
4. promise
To solve the problem of callback hell, Promise came out
const fs = require("fs");
const path = require("path")
const p = new Promise((resolve, reject) = > {
fs.readFile(path.join(__dirname, "./test.txt"), "utf8", (err, data) => {
if (err) {
reject(err);
} else {
resolve(data)
}
})
})
p.then(data= > {
console.log(data);
})
Copy the code
Note that the use and rationale of promise is an important point, which I summarize below
5. generator+co
The generator can be used to interrupt a wakeup execution mechanism.
Why co? As we all know generator is an iterator generator, it is an iterator to call once, and executes the next() method since it is necessary to wake us up. This is tedious. Co encapsulates such an automatic execution for us.
For the sake of chestnut simplicity, the author does not use CO. The main thing is what does it do
const fs = require("fs");
const path = require("path");
const ioPromise = new Promise((resolve, reject) = > {
fs.readFile(path.join(__dirname, "./test.txt"), "utf8", (err, data) => {
if (err) {
reject(err);
} else {
resolve(data)
}
})
});
function* read() {
const data = yield ioPromise;
}
const it = read();
it.next().value.then(data= > {
console.log(data);
});
Copy the code
6. Async/AWIAT (Generator + CO syntax sugar)
This is what we use now
async function getData() {
const res = await this.$aixos("...")}Copy the code
Phase two: Knowledge of promises
1. Common methods
On the prototype
-
Then (function(){… then (function(){… },function(){…. })
The return value needs to be paid attention to (looking directly at MDN, this is worth paying attention to because the logic is important when writing by hand)
- Returns a value, so
then
The returned Promise will become the accepted state, and the returned value will be used as the parameter value of the callback that accepts the state. - No value is returned, then
then
The returned Promise will be the accepted state, and the accepted state callback will take the value ofundefined
. - Throw an error, then
then
The returned Promise will be the rejection state, and the thrown error will be the parameter value of the rejection state callback. - Return a Promise that is already in an accepted state, then
then
The returned Promise also becomes the accepted state, and the value of the Promise’s accepted state callback is used as the value of the returned Promise’s accepted state callback. - Return a Promise that is already in the reject state, then
then
The returned Promise also becomes the rejection state, and the value of the Promise’s rejection state callback is used as the value of the returned Promise’s rejection state callback. - Returns an undetermined state (
pending
), thenthen
The state that returns a Promise is also undefined, and its final state is the same as the final state of that Promise; At the same time, the callback that it calls when it goes to its final state is the same as the callback that the Promise calls when it goes to its final state.
- Returns a value, so
-
Catch (): Alias for then(null,function(){}). The promise state failed to walk it. Note that errors after resolve are not caught, and promise errors have the “bubbling nature” of being passed backwards until they are caught. Therefore, it is recommended to write catch to the end
- Return value: a promise
-
Finally () : Use this method regardless of whether the promise state succeeds or fails
Since the return value of then is also promise, it provides the possibility of chain invocation of promise
A static method
-
Resolve: Returns a Promise object whose state is determined by the given value. If the value is thenable(that is, the object with the THEN method), the final state of the returned Promise object is determined by the then method execution; Otherwise (the value is empty, a basic type, or an object with no THEN method), the Promise object will be returned with the state of FULFILLED and the value will be passed to the corresponding THEN method. In general, if you don’t know if a value is a Promise object, use promise.resolve (value) to return a Promise object so that the value can be used as a Promise object.
-
Reject: Returns a Promise object in a failed state and passes the given failure information to the corresponding processing method
-
All: iterable is an array of promise elements, i.e. [p1,p2,p3]. Several promises all resolve, and the return value of all is also a completed promise. If there is a reject, the operation fails
-
Race: Similar to all, [P1, P2,p3] recognizes whichever promise’s state changes first.
2. Write a promise
The **Promise** object is a proxy object (proxy for a value), and the proxied value may not be known at the time the Promise object is created. It allows you to bind handlers for success and failure of asynchronous operations. This allows asynchronous methods to return values as synchronous methods do, but instead of immediately returning final execution results, a Promise object that represents future results
A Promise has the following states:
- pending: Initial state, neither successful nor failed state.
- fulfilled: indicates that the operation completed successfully.
- rejected: Indicates that the operation fails.
Before you write it, think back to how promises are used. We have an execution function in new that executes resolve if the asynchronous logic succeeds and reject if it fails
That is, the state of a promise is pending at the beginning, and once it is implemented, it becomes a pity, or once it is implemented, it becomes rejected
And the promise state changes and never changes again. Success is success. It doesn’t go from a successful state to a failed state
1 to construct this class (this is already the simplest version)
const PENDING = "PENDING"; / / run state
const SUCCESS = "SUCCESS"; / / success
const FUL = "FUL"; / / failure mode
class MyPromise {
constructor(exector) {
this.status = PENDING;
this.res = undefined; // Failure cause
this.val = undefined; / / success
let resolve = (val) = > {
if (this.status ! = PENDING)return;
this.val = val;
this.status = SUCCESS;
}
let reject = (err) = > {
if (this.status ! = PENDING)return;
this.res = err;
this.status = FUL;
}
try {
exector(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onfulfilled, omrejected) {
if (this.status === SUCCESSS) {
onfulfilled(this.val)
}
if (this.status === FUl) {
omrejected(this.res); }}}Copy the code
2. Add asynchronous solution
The above implementation, if an asynchronous task. The state of then is still PENDING.
Use publish subscribe mode, or publish signal when asynchronous results come out. Then implement subscription
class MyPromise {
constructor(exector) {
this.status = PENDING;
this.res = undefined;
this.val = undefined;
// then successful callback
this.onfulfilledCb = [];
// install then failed callback
this.onrejectedCb = [];
let resolve = (val) = > {
if (this.status ! = PENDING)return;
this.val = val;
this.status = SUCCESS;
// Execute subscribed callback after successful result
this.onfulfilledCb.forEach(fn= > fn(val))
}
let reject = (err) = > {
if (this.status ! = PENDING)return;
this.res = err;
this.status = FUL;
// Execute subscribed callback when failure results
this.onrejectedCb.forEach(fn= > fn(err));
}
try {
exector(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onfulfilled, onrejected) {
if (this.status === SUCCESS) {
onfulfilled(this.val);
}
if (this.status === FUL) {
onrejected(this.res);
}
/ / asynchronous
if (this.status === PENDING) {
this.onfulfilledCb.push(onfulfilled);
this.onrejectedCb.push(onrejected); }}}Copy the code
3. To ensure that the chain call then must return a promise
Note the return type of then in the common methods above. The setTimeout package is used because the Promise specification explicitly states that the THEN method is executed asynchronously, so setTimeout is used to simulate this
const resolvePromise = (promise2, x, resolve, reject) = > {
if (typeof x === "object" || typeof x === "function") {
let then = x.then;
// The then method defaults to promise
if (typeof then === "function") {
then.call(x, y => {
// it may return a promise, so recursive processingresolvePromise(promise2, y, resolve, reject) }, z => { reject(z); }}})else {
// A simple type's value is resolvedresolve(x); }}const PENDING = "PENDING";
const SUCCESS = "SUCCESS";
const FUL = "FUL";
class MyPromise {
constructor(exector) {
this.status = PENDING;
this.res = undefined;
this.val = undefined;
this.onfulfilledCb = [];
this.onrejectedCb = [];
let resolve = (val) = > {
if (this.status ! = PENDING)return;
this.val = val;
this.status = SUCCESS;
this.onfulfilledCb.forEach(fn= > fn())
}
let reject = (err) = > {
if (this.status ! = PENDING)return;
this.res = err;
this.status = FUL;
this.onrejectedCb.forEach(fn= > fn());
}
try {
exector(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onfulfilled, onrejected) {
let promise2 = new MyPromise((resolve, reject) = > {
if (this.status === SUCCESS) {
setTimeout((a)= > {
let x = onfulfilled(this.val);
resolvePromise(promise2, x, resolve, reject)
}, 0)}if (this.status === FUL) {
setTimeout((a)= > {
let x = onrejected(this.res);
resolvePromise(promise2, x, resolve, reject)
}, 0)}/ / asynchronous
if (this.status === PENDING) {
this.onfulfilledCb.push((a)= > {
setTimeout((a)= > {
let x = onfulfilled(this.val);
resolvePromise(promise2, x, resolve, reject)
}, 0)});this.onrejectedCb.push((a)= > {
setTimeout((a)= > {
let x = onrejected(this.res);
resolvePromise(promise2, x, resolve, reject)
}, 0)}); }})returnpromise2; }}Copy the code
4 All codes
const resolvePromise = (promise2, x, resolve, reject) = > {
if (typeof x === "object" || typeof x === "function") {
let then = x.then;
// The then method defaults to promise
if (typeof then === "function") {
then.call(x, y => {
// it may return a promise, so recursive processingresolvePromise(promise2, y, resolve, reject) }, z => { reject(z); }}})else {
// A simple type's value is resolvedresolve(x); }}const PENDING = "PENDING";
const SUCCESS = "SUCCESS";
const FUL = "FUL";
class MyPromise {
constructor(exector) {
this.status = PENDING;
this.res = undefined;
this.val = undefined;
this.onfulfilledCb = [];
this.onrejectedCb = [];
let resolve = (val) = > {
if (this.status ! = PENDING)return;
this.val = val;
this.status = SUCCESS;
this.onfulfilledCb.forEach(fn= > fn())
}
let reject = (err) = > {
if (this.status ! = PENDING)return;
this.res = err;
this.status = FUL;
this.onrejectedCb.forEach(fn= > fn());
}
try {
exector(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onfulfilled, onrejected) {
let promise2 = new MyPromise((resolve, reject) = > {
if (this.status === SUCCESS) {
setTimeout((a)= > {
let x = onfulfilled(this.val);
resolvePromise(promise2, x, resolve, reject)
}, 0)}if (this.status === FUL) {
setTimeout((a)= > {
let x = onrejected(this.res);
resolvePromise(promise2, x, resolve, reject)
}, 0)}/ / asynchronous
if (this.status === PENDING) {
this.onfulfilledCb.push((a)= > {
setTimeout((a)= > {
let x = onfulfilled(this.val);
resolvePromise(promise2, x, resolve, reject)
}, 0)});this.onrejectedCb.push((a)= > {
setTimeout((a)= > {
let x = onrejected(this.res);
resolvePromise(promise2, x, resolve, reject)
}, 0)}); }})returnpromise2; }}let p = new MyPromise((resolve, reject) = > {
setTimeout((a)= > {
resolve(1000)},1000)}); p.then(data= > {
console.log(data);
return new MyPromise((resolve, reject) = > {
setTimeout((a)= > {
resolve(1000)},1000)
})
}).then(data= > {
console.log(data);
})
Copy the code