interviewer : Writing a Promise
, Requirement realization resolve()/reject()/then()
……
One Catalog
No frills front end , What's the difference with salted fish
Two Myth
Return directory
JavaScript In the evolution of asynchronous solutions in :
callback -> promise -> generator -> async/await
In the computer industry , There is a myth of simple reductionism : That is, the closer we get to the bottom , The higher the technical content .
Every programmer has the pursuit of understanding the underlying source code .
This is true to a certain extent .
however , We should see that too , Once between the bottom and the surface , There's a domain gap .
Master the bottom , It doesn't mean at the surface level .
For example, game developers , Not necessarily the best in the game . This is in FPS
Especially in shooting or fighting games , Most of the top players in these games , I can't write code at all .
If you will be proficient in Promise
Defined as , Good at using in various asynchronous scenarios Promise
solve the problem .
that , Be able to write Promise
Realization , Proficient in Promise
It doesn't help much .
This text comes from Industrial Agglomeration
3、 ... and Simple handwriting Promise
Return directory
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
function myPromise(fn) {
const that = this;
that.status = PENDING;
that.value = null;
that.reason = null;
that.resolvedCallbacks = [];
that.rejectedCallbacks = [];
function resolve(value) {
if (that.status === PENDING) {
that.status = RESOLVED;
that.value = value;
that.resolvedCallbacks.map(cb => cb(value));
}
}
function reject(reason) {
if (that.status === PENDING) {
that.status = REJECTED;
that.reason = reason;
that.rejectedCallbacks.map(cb => cb(reason));
}
}
try {
fn(resolve, reject);
} catch(e) {
reject(e);
}
}
myPromise.prototype.then = function(onFullfilled, onRejected) {
const that = this;
if (that.status === PENDING) {
that.resolvedCallbacks.push(onFullfilled);
that.rejectedCallbacks.push(onRejected);
}
if (that.status === RESOLVED) {
onFullfilled(that.value);
}
if (that.status === RESOLVED) {
onFullfilled(that.reason);
}
return that;
}
const p = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(1000);
}, 1000);
});
p.then((res) => {
console.log(' result :', res); // result :1000
}).then(() => {
console.log('jsliang'); // jsliang
})
This code is in jsliang This is the personal understanding of :
- Yes
pending
、resolved
as well asrejected
this 3 Status . See before the code 3 That's ok . - Once the state is changed from
pending
towardsresolve
perhapsrejected
shift , Then it can't change again . See the code for details.function resolve
andfunction reject
. - Code execution to
.then
When , It is divided into 2 In this case : One is to go asynchronous , The state has becomePENDING
, Take the first logic ; The second isRESOLVED
perhapsRESOLVED
, We went through the second and third logic . - In the case of the first logic , Because we're asynchronous , So in the
n
In secondsfunction resolve
perhapsfunction reject
, And in these two methods ,that.resolvedCallbacks.map(cb => cb(value))
perhapsthat.rejectedCallbacks.map(cb => cb(reason))
It will execute the callback method that we have saved , So as to achievePromise.then()
The function of .
If the partner is not clear enough to type the code twice more , If my little friend is right jsliang Not interested in understanding , Look at the references Promise A+
standard , It's more conducive to the understanding of small partners .
Four Handwriting Promise
Return directory
Code source industry gather that article :
At first, I thought the code was very sound , But let jsliang It's really hard for me to write silently , So I used the code from the previous chapter .
/**
* @name JsliangPromise
* @description Handwriting Promise
*/
/* ——————————————————————————————————————————————— Set common functions ——————————————————————————————————————————————— */
// Judge if it's a function
const isFunction = (obj) => {
return typeof obj === 'function';
};
// Judge whether it is the object
const isObject = (obj) => {
return !!(obj && typeof obj === 'object');
};
// Judge whether it is thenable
const isThenable = (obj) => {
return (
isFunction(obj)
|| isObject(obj)
) && 'then' in obj;
};
// Judge whether it is Promise
const isPromise = (promise) => {
return promise instanceof JsliangPromise;
};
/* ——————————————————————————————————————————————— Set up Promise state ——————————————————————————————————————————————— */
/**
1. Promise Yes 3 Status :pending、fulfilled、rejected
* pending:Promise You can switch to fulfilled perhaps rejected state
* fulfilled: Can't migrate to other states , There has to be an immutable value
* rejected: Can't migrate to other states , There has to be an immutable reason
*/
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
/* ——————————————————————————————————————————————— Promise Supplementary treatment ——————————————————————————————————————————————— */
// 3.6 handleCallback function , according to state state , Judgment is to go fulfilled The path is still rejected route
const handleCallback = (callback, state, result) => {
// 3.6.1 deconstruction
const { onFulfilled, onRejected, resolve, reject } = callback;
// 3.6.2 Judge
try {
// 3.6.3 If it's successful
if (state === FULFILLED) {
// 3.6.4 Judge onFulfilled Is it a function
if (isFunction(onFulfilled)) {
// 3.6.5 If it is , Take its return value as the next Promise Of result
resolve(onFulfilled(result));
} else {
// 3.6.6 If not , Directly to the present Promise Of result As the next Promise Of result
resolve(result);
}
} else if (state === REJECTED) {
if (isFunction(onRejected)) {
resolve(onRejected(result));
} else {
reject(result);
}
}
} catch (error) {
// 3.6.7 If the execution process throws a mistake , Then this mistake , As the next Promise Of rejected reason To use
reject(error);
}
};
// 2.3.4 Clear the contents before
const handleCallbacks = (callbacks, state, result) => {
while (callbacks.length) {
handleCallback(callbacks.shift(), state, result);
}
};
// 2.3 Once the state is not pending, It's not allowed to switch again
const transition = (promise, state, result) => {
// 2.3.1 If it is pending, Then it should be corresponding state and result
if (promise.state !== PENDING) {
return;
}
// 2.3.2 If not , Then set it up
promise.state = state;
promise.result = result;
// 2.3.3 When the state changes , Asynchronously empties all callbacks
setTimeout(() => {
handleCallbacks(promise.callbacks, state, result);
}, 0);
}
// 2.6 Some special value By resolve when , Special treatment is needed .
// This special treatment , The specification also clearly describes
const resolvePromise = (promise, result, resolve, reject) => {
// 2.6.1 If result It is the present. Promise In itself , Throw out TypeError error
if (result === promise) {
return reject(new TypeError('Can not fulfill promise with itself'));
}
// 2.6.2 If result Is another Promise, So use the current state and result state
if (isPromise(result)) {
return result.then(resolve, reject);
}
// 2.6.3 If result It's a thenable object .
// Go first then function , Again call then function , Re enter The Promise resolution procedure The process
if (isThenable(result)) {
try {
if (isFunction(result.then)) {
return new JsliangPromise(then.bind(result)).then(resolve, reject);
}
} catch (error) {
return reject(error);
}
}
// 2.6.4 If not , direct resolve result
resolve(result);
};
/* ——————————————————————————————————————————————— Promise Realization ——————————————————————————————————————————————— */
// 2. Set up Promise
const JsliangPromise = function(f) {
// 2.1 Set the initialization state
this.state = PENDING;
this.result = null;
// 3.1 .then() Can be called multiple times , So you need to set the array to record
this.callbacks = [];
// 2.2 structure onFulfilled To switch to fulfilled, structure onRejected To switch to rejected state
const onFulfilled = value => transition(this, FULFILLED, value);
const onRejected = reason => transition(this, REJECTED, reason);
// 2.3 coordination ignore To guarantee resolve/reject There is only one call to function
let ignore = false;
// 2.4 Set up resolve Treatment mode
let resolve = (value) => {
if (ignore) {
return;
}
ignore = true;
// 2.5 Yes resolve Conduct 3 Rules determine
resolvePromise(this, value, onFulfilled, onRejected);
};
let reject = (reason) => {
if (ignore) {
return;
}
ignore = true;
onRejected(reason);
}
// 2.6 Try
try {
// 2.6.1 take resolve and reject Pass in as a parameter f function , Convenient to call
f(resolve, reject);
} catch (error) {
// 2.6.2 If f Function execution error , Then the mistake is taken as reject Of reason To use
reject(error);
}
}
// 3. Promise.then Method
JsliangPromise.prototype.then = function (onFulfilled, onRejected) {
// 3.2 .then() Method returns Promise, So we need to return One goes out
return new JsliangPromise((resolve, reject) => {
// 3.3 Set up callback
const callback = { onFulfilled, onRejected, resolve, reject };
// 3.4 If state be in pending state , Just store it in callbacks In the list
if (this.state === PENDING) {
this.callbacks.push(callback);
} else {
// 3.5 If not , Just throw one handleCallback To deal with
// As for why setTimeout? Because we can't simulate micro tasks , Then use macro task to solve it
setTimeout(() => {
handleCallback(callback, this.state, this.result);
}, 0);
}
});
};
/* ——————————————————————————————————————————————— test ——————————————————————————————————————————————— */
const promise = new JsliangPromise((resolve, reject) => {
setTimeout(() => {
console.log(1);
resolve(2);
}, 1000);
});
promise.then((res) => {
console.log('res 1:', res);
return 3;
}).then((res) => {
console.log('res 2:', res);
});
5、 ... and Promise.all()
Return directory
Let's practice some topics .
Given the content :
const a = new Promise((resolve) => {
setTimeout(() => {
resolve(500);
}, 500);
});
const b = new Promise((resolve) => {
setTimeout(() => {
resolve(1000);
}, 1000);
});
Promise.myAll([a, b]).then((res) => {
console.log(' result :', res);
});
Request output result :[ 500, 1000 ]
.
Promise.myAll = function(arr) {
// 1. Return to one Promise
return new Promise((resolve, reject) => {
// 2. Set the final return result
const result = [];
// 3. Get the length of the array and the current progress index index
const length = arr.length;
let index = 0;
// 4. Traversal array , Go through everything in it
for (let i = 0; i < arr.length; i++) {
// 5 stay .then It's for result Set contents
// If index At the end , then resolve(result)
// otherwise reject(err)
arr[i].then((res) => {
result[i] = res;
index++;
if (index === length) {
resolve(result);
}
}).catch((err) => {
throw new Error(err);
})
}
})
};
6、 ... and Promise.race()
Return directory
Given the content :
const a = new Promise((resolve) => {
setTimeout(() => {
resolve(500);
}, 500);
});
const b = new Promise((resolve) => {
setTimeout(() => {
resolve(1000);
}, 1000);
});
Promise.myRace([a, b]).then((res) => {
console.log(' result :', res);
});
Request output result :500
.
race
Method and previous all
The method is similar to , The difference is that it was executed the first time .then
It's OK to return the result directly , You don't have to return every result .
Promise.myRace = function(arr) {
return new Promise((resolve, reject) => {
for (let i = 0; i < arr.length; i++) {
arr[i].then((res) => {
return resolve(res);
}).catch((err) => {
throw new Error(err);
})
}
})
};
const a = new Promise((resolve) => {
setTimeout(() => {
resolve(500);
}, 500);
});
const b = new Promise((resolve) => {
setTimeout(() => {
resolve(1000);
}, 1000);
});
Promise.myRace([a, b]).then((res) => {
console.log(' result :', res);
})
7、 ... and Promise Asynchronous scheduler
Return directory
Review the question and complete the following code :
/**
* subject :JS Implement asynchronous scheduler
* requirement :
* JS Implement an asynchronous scheduler with concurrency constraints Scheduler, The maximum number of tasks guaranteed to run simultaneously is 2 individual
* Perfect the following code Scheduler class , Make the program output correctly
*/
class Scheduler {
add(promiseCreator) {
// ...
}
// ...
}
const timeout = (time) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
};
const scheduler = new Scheduler();
const addTack = (time, order) => {
return scheduler
.add(() => timeout(time))
.then(() => console.log(order));
};
addTack(1000, '1');
addTack(500, '2');
addTack(300, '3');
addTack(400, '4');
// Output :2 3 1 4
// In limine ,1、2 Two tasks enter the queue
// 500ms when , complete 2, Output 2, Mission 3 Enter the team
// 800ms when , complete 3, Output 3, Mission 4 Enter the team
// 1000ms when , complete 1, Output 1, There's no one in the team
// 1200ms when , complete 4, Output 4, There's no one in the team
// Team entry complete , Output 2 3 1 4
Realization way (async/await
):
/**
* subject :JS Implement asynchronous scheduler
* requirement :
* JS Implement an asynchronous scheduler with concurrency constraints Scheduler, The maximum number of tasks guaranteed to run simultaneously is 2 individual
* Perfect the following code Scheduler class , Make the program output correctly
*/
class Scheduler {
constructor(maxNum) {
this.taskList = [];
this.count = 0;
this.maxNum = maxNum; // Maximum number of concurrent
}
async add(promiseCreator) {
// If the current concurrency exceeds the maximum concurrency , Then enter the task queue and wait
if (this.count >= this.maxNum) {
await new Promise((resolve) => {
this.taskList.push(resolve);
})
}
// frequency + 1( If the previous one is not finished , Then keep adding )
this.count++;
// Wait for the content to be executed
const result = await promiseCreator();
// frequency - 1
this.count--;
// Get the team out of the team first
if (this.taskList.length) {
this.taskList.shift()();
}
// call chaining , Return the result value
return result;
}
}
const timeout = (time) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
};
const scheduler = new Scheduler(2);
const addTack = (time, order) => {
return scheduler
.add(() => timeout(time))
.then(() => console.log(order));
};
addTack(1000, '1');
addTack(500, '2');
addTack(300, '3');
addTack(400, '4');
// Output :2 3 1 4
// In limine ,1、2 Two tasks enter the queue
// 500ms when , complete 2, Output 2, Mission 3 Enter the team
// 800ms when , complete 3, Output 3, Mission 4 Enter the team
// 1000ms when , complete 1, Output 1, There's no one in the team
// 1200ms when , complete 4, Output 4, There's no one in the team
// Team entry complete , Output 2 3 1 4
8、 ... and reference
Return directory
- [x] 100 Line code implementation Promises/A+ standard 【 Reading suggestions :30min】
- [x] The simplest way to achieve Promise, Support asynchronous chain call (20 That's ok )【 Recommended reading :20min】
- [x] BAT Front end classic interview questions : The most detailed handwriting in history Promise course 【 Reading suggestions :30min】
- [x] Learn to make wheels together ( One ): Write a match from scratch Promises/A+ canonical promise【 Reading suggestions : I've probably seen it all over , It's not as clear as the previous analysis 】
- [x] Promise Realization principle ( Source code attached )【 Reading suggestions : I've probably seen it all over , It's not as clear as the previous analysis 】
- [x] analyse Promise internal structure , Step by step to achieve a complete 、 Through all Test case Of Promise class 【 Recommended reading : It's written in more detail , It's not as clear as the previous analysis 】
- [x] Xiao Shao teaches you to play promise Source code 【 Recommended reading : It's written in more detail , It's not as clear as the previous analysis 】
- [x] Promise Can't ?? Look here !!! The most accessible thing in history Promise!!!【 Recommended reading : It's written in more detail , It's not as clear as the previous analysis 】
jsliang The document library of is made up of Liang Junrong use Knowledge sharing A signature - Noncommercial use - Share in the same way 4.0 The international license agreement Licensing .<br/> be based on https://github.com/LiangJunrong/document-library Creation of works on .<br/> Use rights other than those authorized by this license agreement can be obtained from https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ To obtain .