当前位置:网站首页>Deeply understand promise's hand and teach you to write a version
Deeply understand promise's hand and teach you to write a version
2022-06-29 15:22:00 【User 1097444】
What is? Promise?
- Grammatically :
PromiseIt's a constructor , Returns an object with a state - The function :
PromiseIt is used to solve asynchronous functions and make different responses according to the results - Specification :
PromiseIt's a possessionthenObject of method ( stay JS Functions are also objects )
Why use Promise?
One of the biggest headaches on the front end is dealing with asynchronous requests :
function load() {
$.ajax({
url: 'xxx.com',
data: 'jsonp',
success: function(res) {
init(res, function(res) {
render(res, function(res) {
// Thousand layer cake
});
});
}
}
}Multiple code levels , Poor readability and difficult to maintain , Form a callback hell .
With Promise, We can use the process of synchronous operation to write asynchronous operation , Solve the problem of nested callback functions :
new Promise(
function (resolve, reject) {
// A time-consuming asynchronous operation
resolve(' success ')
or
reject(' Failure ')
}
).then(
res => {console.log(res)}, // success
).catch(
err => {console.log(err)} // Failure
) Of course ,Promise There are also shortcomings.
- Can't cancel
Promise, Once you create a new one, it will be executed immediately , Unable to cancel - If you do not set the callback function , Can't throw
PromiseInternal error to external - When in
PendingIn the state of , Unable to know the current operation , Is it just the beginning or the end
This matter should not be delayed , We'll start right away !
Promise The state of
Promise There are the following 3 States :
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected' The state can only be determined by pending towards fulfilled or rejected shift , And only once if the execution environment stack contains only platform code , It is called state solidification , And save a parameter to indicate the result .
this.value = value // fulfilled state , Save the final value
this.reason = reason // rejected state , Save the reason Promise Constructors
promise The constructor takes a function as an argument , We call this function parameter executor, stay promise Execution time , Will send to executor Pass in two function parameters , Respectively resolve and reject, They only do 3 thing :
- change
promisestate - preservation
value/reasonresult - perform
onFulfilled/onRejectedCallback function
The third one is then Method , There will be no more discussion here , Look at the first two , Just two lines of code :
this.state = state
this.value = value / this.reason = reasonLet's start with a simple constructor :
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise{
constructor(executor) {
this.state = PENDING // prpmise Having an initial state
this.value = null// Used to save the final value
this.reason = null// Used to save the rejection
this.onFulfilledCallbacks = [] // Successfully callback the queue
this.onRejectedCallbacks = [] // Failed callback queue
// Definition resolve function
// The arrow function is used here to solve this The direction of , If you don't know, you can see Ruan dada's ES6 article
const resolve = value => {
// The guarantee status can only be changed once
if (this.state === PENDING) {
this.state = FULFILLED
this.value = value
}
}
// ditto
const reject = reason => {
if (this.state === PENDING) {
this.state = REJECTED
this.reason = reason
}
}
// executor There may be exceptions , You need to capture and call reject The function indicates that the execution failed
try {
// Pass in two function parameters
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
} It looks good , The general process has been completed . I remember saying , The change of state is when the main thread is idle , Use here setTimeout To simulate the , as well as resolve/reject There's a third left 3 thing , Now let's improve it :
const resolve = value => {
// setTimeout simulation
// Be careful Even if it is to judge whether the state is pending It should also be executed when the main thread is idle
setTimeout(() => {
if (this.state === PENDING) {
this.state = FULFILLED
this.value = value
// If you use forEach Callback functions may not execute in order
this.onFulfilledCallbacks.map(cb => cb(this.value))
}
})
}
// reject ditto All right. , A complete constructor is written , Do you feel very relaxed ,Promise nothing more than this .
Next is the play then Method ,then Accept two function arguments , Respectively onFulfilled/onRejected, Used to configure promise Callback function after state change .
It has two main points :
- Return to one
promise2, To achieve chained calls
- among
promise2The state of must solidify - adopt
resolvePromiseFunction andonFulfilled/onRejectedThe return value ofpromise2Solidification state
- Listen or execute the corresponding
onFulfilled/onRejectedCallback function
- If it is executed, you need to put
event-loop - Listening only needs to be pushed into the callback function array
Aforementioned resolvePromise Let's ignore , Just know that it is used to decide promise2 The status of .
First ,then I need to return a promise2:
then(onFulfilled, onRejected) {
let promise2
return (promise2 = new MyPromise((resolve, reject) => {
})
} secondly ,then The purpose of the method is to configure or execute the corresponding onFulfilled/onRejected Callback function :
then(onFulfilled, onRejected) {
let promise2
return (promise2 = new MyPromise((resolve, reject) => {
// Configure the callback function and push it into the corresponding callbacks Array
this.onFulfilledCallbacks.push(value => {
// The first step in configuration : perform callback And save the return value x
let x = onFulfilled(value);
// Configure the second step : adopt resolvePromise decision promise2 state
resolvePromise(promise2, x, resolve, reject)
})
// onRejected ditto
this.onRejectedCallbacks.push(reason => {
let x = onRejected(reason)
resolvePromise(promise2, x, resolve, reject)
})
})
} Here you can get a general idea of resolvePromise How did it change promise2 State of , It accepts promise2 Of resolve/reject, Due to the arrow function ,resolve/reject Of this Point to still point to promise2, So that we can go through resolvePromise To change the State .
In case onFulfilled/onRejected What to do if something goes wrong ? We need to capture it and promise2 Change the status of rejected, We will modify the code again :
then(onFulfilled, onRejected) {
let promise2
return (promise2 = new MyPromise((resolve, reject) => {
// Configure the callback function and push it into the corresponding callbacks Array
this.onFulfilledCallbacks.push(value => {
try {
let x = onFulfilled(value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
// onRejected ditto
this.onRejectedCallbacks.push(reason => {
try {
let x = onRejected(reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
} If the then The method is to solidify promise Well , Also push callbacks Array ? The answer, of course, is not , Instead, the configured onFulfilled/onRejected Throw in event-loop in , Don't bother resolve/reject 了 :
then(onFulfilled, onRejected){
// fulfilled state , Throw the configured callback function into event-loop
if (this.state === FULFILLED) {
return (promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}))
}
// rejected The status is the same as above
if (this.state === REJECTED) {
return (promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}))
}
// pending The state is left to resolve/reject To decide
if (this.state === PENDING) {
return (promise2 = new MyPromise((resolve, reject) => {
this.onFulfilledCallbacks.push(value => {
try {
let x = onFulfilled(value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
this.onRejectedCallbacks.push(reason => {
try {
let x = onRejected(reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}))
}
}
} It looks perfect , But there's one small thing missing , If promise The user does not play cards according to the routine , Incoming onFulfilled/onRejected What if it's not a function ? Here we will directly return it as the return value :
then(onFulfilled, onRejected){
let promise2
// Make sure onFulfilled/onRejected For the function
// If not... Function , Then it is converted to a function and the return value is itself
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => {
throw reason
}
if (this.state === FULFILLED) {
return (promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}))
}
if (this.state === REJECTED) {
return (promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}))
}
if (this.state === PENDING) {
return (promise2 = new MyPromise((resolve, reject) => {
this.onFulfilledCallbacks.push(value => {
try {
let x = onFulfilled(value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
this.onRejectedCallbacks.push(reason => {
try {
let x = onRejected(reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}))
}
}
}Be accomplished !
There's only one left resolvePromise Method , First introduce its function : According to the return value of the callback function x decision promise2 The final state of :
- If
xbythenableobject , Instant beltthenObject of method- Yes , Because it does not necessarily meet
promiseStandards for , Let's make more preparations - nothing , Execute as a normal value
- Use
calledVariable makes its state change only happen once - Abnormal monitoring
- Recursively call resolvePromise To prevent Dolls
- If
xbypromise, Then recursively call , Until the return value is a normal value - If
xFor a function or object , Judge whether it exists or notthenMethod
- Yes , Because it does not necessarily meet
xFor ordinary value- Go straight back to
Let's analyze it step by step :
function resolvePromise(promise2, x, resolve, reject){
// First from x by thenable Object start
// If x === promise2 You need to throw a circular reference error , Otherwise, there will be a dead cycle
if (x === promise2) {
reject(newTypeError(' Circular reference '))
}
// If x Namely promise
// according to x To determine the state of promise2 The state of
if (x instanceof MyPromise) {
// x Status as PENDING when
// When x By resolve Will call the new resolvePromise
// Because of fear resolve The final value saved is still promise Continue to set the baby
// So be sure to call recursively resolvePromise Ensure that the final returned value must be a normal value
// Fail to call directly reject that will do
if (x.state === PENDING) {
x.then(
y => {
resolvePromise(promise2, y, resolve, reject)
},
r => {
reject(r)
}
)
} else {
// x State solidification , Just configure it directly
// But here's a question
// If before resolve The final value saved is still promise Well
// How to prevent this problem , Later we will talk about
x.then(resolve, reject)
}
}
} Now deal with x The value of is promise Your code is written , But that's not enough , We have to face more than promise, It is a thenable object , So continue to judge :
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
reject(newTypeError(' Circular reference '))
}
if (x instanceof MyPromise) {
// The previous code will not repeat
} elseif (x && (typeof x === 'function' || typeof x === 'object')) {
// Because it is not necessarily standardized promise object
// We need to make sure that the change of state only happens once
// Add a called Variables to lock
let called = false
// Or because it is not necessarily standardized promise object
// You need to ensure that runtime exceptions can be caught
try {
// Be careful , Not in front try/catch
// Just the following line of code may also report an error and cannot be captured
let then = x.then
// If x.then Exists and is a function
if (typeof then === 'function') {
// Use call The method guarantees then The calling object of is x
then.call{
x,
y => {
// If the state solidifies, it will no longer execute
if (called) return
called = true
// Prevent emergence resolve preservation promise The situation of
resolvePromise(promise2, y, resolve, reject)
},
r => {
// ditto
if (called) return
called = true
reject(r)
}
}
} else {
// If x.then It's not a function
// Is the normal value , direct resolve Just fine
resolve(x)
}
} catch (e) {
// If you call an irregular thenalbe Object error
// Throw an exception
// Pay attention here , The error here is likely to be the execution of x.then Method , And I said before , It is not necessarily formal , Maybe the state has solidified , We need extra insurance
if (called) return
called = true
reject(e)
}
} else {
// No thenable object , That's the normal value
// direct resolve
resolve(x)
}
} Write down a set of flowing code , our promise It's done. , But remember the question left in the code before ? When x by promise And when the state is solidified , If it is determined that the final value saved is not promise Well ? In fact, as long as the beginning resolve Add one more judgment to the function :
const resolve = value => {
if (value instanceof MyPromise) {
return value.then(resolve, reject)
}
setTimeout(() => {
if (this.state === PENDING) {
this.state = FULFILLED
this.value = value
this.onFulfilledCallbacks.map(cb => cb(this.value))
}
})
}Prevent the dolls again !
All right. , Maybe you'll ask , How do I know this handwritten promise It must be right ? Next, I will take you step by step to verify !
First find an empty folder , Enter at the command line :
npm init -y
// download promise Testing tools
npm install promises-aplus-tests -D newly build promise.js file , And realize your promise Copy here , And add the following code below :
MyPromise.deferred = function() {
let defer = {}
defer.promise = new MyPromise((resolve, reject) => {
defer.resolve = resolve
defer.reject = reject
});
return defer
}
module.exports = MyPromise Revise package.json The documents are as follows :
{
"name": "promise",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "promises-aplus-tests ./promise.js"
},
"devDependencies": {
"promises-aplus-tests": "^2.1.2"
}
}The last step :
npm run testcomplete !
Handwriting Promise, You can also !
Finally, the complete implementation code is attached
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
this.state = PENDING
this.value = null
this.reason = null
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
const resolve = value => {
if (value instanceof MyPromise) {
return value.then(resolve, reject)
}
setTimeout(() => {
if (this.state === PENDING) {
this.state = FULFILLED
this.value = value
this.onFulfilledCallbacks.map(cb => cb(this.value))
}
})
}
const reject = reason => {
setTimeout(() => {
if (this.state === PENDING) {
this.state = REJECTED
this.reason = reason
this.onRejectedCallbacks.map(cb => cb(this.reason))
}
})
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onFulfilled, onRejected) {
let promise2
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => {
throw reason
}
if (this.state === FULFILLED) {
return (promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}))
}
if (this.state === REJECTED) {
return (promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}))
}
if (this.state === PENDING) {
return (promise2 = new MyPromise((resolve, reject) => {
this.onFulfilledCallbacks.push(value => {
try {
let x = onFulfilled(value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
this.onRejectedCallbacks.push(reason => {
try {
let x = onRejected(reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}))
}
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
reject(newTypeError(' Circular reference '))
}
if (x instanceof MyPromise) {
if (x.state === PENDING) {
x.then(
y => {
resolvePromise(promise2, y, resolve, reject)
},
r => {
reject(r)
}
)
} else {
x.then(resolve, reject)
}
} elseif (x && (typeof x === 'function' || typeof x === 'object')) {
let called = false
try {
let then = x.then
if (typeof then === 'function') {
then.call(
x,
y => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
},
r => {
if (called) return
called = true
reject(r)
}
)
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
MyPromise.deferred = function() {
let defer = {}
defer.promise = new MyPromise((resolve, reject) => {
defer.resolve = resolve
defer.reject = reject
});
return defer
};
module.exports = MyPromise;Keep up with the technological frontier , Dig deep into professional fields
Scan the code and pay attention to us !
边栏推荐
- LeetCode笔记:Biweekly Contest 81
- 复数卷积神经网络:CV-CNN
- Lumiprobe 活性染料丨环炔染料:AF488 DBCO,5 异构体
- message from server: “Host ‘xxxxxx‘ is blocked because of many connection errors; unblock with ‘m
- 使用自定义注解实现Redis分布式锁
- c#Sqlite类库
- What is the relationship between synchronized and multithreading
- Flink SQL任务TaskManager内存设置
- 打造一个 API 快速开发平台,牛逼!
- BioVendor游离轻链(κ和λ)Elisa 试剂盒的化学性质
猜你喜欢

Solution to the problem that the assembly drawing cannot be recognized after the storage position of SolidWorks part drawing is changed

What is the relationship between synchronized and multithreading

Lumiprobe reactive dye carboxylic acid: sulfo cyanine7.5 carboxylic acid

Take another picture of cloud redis' improvement path

Unity C # basic review 26 - first acquaintance Commission (p447)

Hi, you have a code review strategy to check

Hi,你有一份Code Review攻略待查收

Real software testers = "half product + Half development"?

Knowledge points: what are the know-how of PCB wiring?

MySQL的json 数组操作 json_array_append、json_array_insert
随机推荐
MCS:多元随机变量——离散随机变量
SSL V**技术原理
LeetCode笔记:Biweekly Contest 81
Const usage
JS 会有变量提升和函数提升
MySQL开发规范.pdf
ROS notes (10) - Launch file startup
Unity C basic review 27 - delegation example (p448)
MySQL为什么选择B+树存储索引
深度学习网络的训练方式
Construction and application of medical field Atlas of dingxiangyuan
What should phpcms do when it sends an upgrade request to the official website when it opens the background home page?
Knowledge points: what are the know-how of PCB wiring?
遥感典型任务分析
雷达天线简介
BioVendor遊離輕鏈(κ和λ)Elisa 試劑盒的化學性質
信息学奥赛一本通1001:Hello,World!
Leetcode notes: biweekly contest 81
CKS CKA ckad change terminal to remote desktop
如临现场的视觉感染力,NBA决赛直播还能这样看?