Interviewer: Write a Promise, resolve()/reject()/then()…
A directory
What’s the difference between a free front end and a salted fish
directory |
---|
A directory |
The second myth |
Three simple handwritten promises |
Four written Promise |
Five Promise. All () |
Six Promise. Race () |
Promise asynchronous scheduler |
Viii References |
The second myth
The evolution of asynchronous schemes in JavaScript:
callback -> promise -> generator -> async/await
The myth of naive reductionism prevails in the computer industry: the closer you get to the bottom, the more skilled you get.
Every programmer has a desire to read the underlying source code.
This is partly true.
However, we should also see that once there is a domain gap between the bottom layer and the surface layer.
Mastery of the bottom level does not mean mastery of the surface level.
For example, the developer of the game is not necessarily the best in the game. This is especially true in FPS shooters or fighting games, where the vast majority of the best players can’t write code at all.
If you define mastery of promises as being good at using promises to solve problems in a variety of asynchronous scenarios.
So, being good at writing Promise implementations doesn’t do much for mastering promises.
This text is from industrial poly
Three simple handwritten promises
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 === REJECTED) {
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
})
Copy the code
This code reads like this on Jsliang’s personal understanding:
- There are
pending
,resolved
As well asrejected
These three states. See the first three lines of this code. - Once the state is
pending
向resolve
orrejected
Change, then you cannot change again. See the codefunction resolve
和function reject
. - Code execution to
.then
Is divided into two cases: one is to go asynchronous, the state becomesPENDING
, take the first logic; The second isRESOLVED
orREJECTED
“, takes the second and third logic. - In the case of the first logic, because we’re in an asynchronous scenario, we’re in
n
Seconds later gofunction resolve
orfunction reject
And in the two method bodies,that.resolvedCallbacks.map(cb => cb(value))
orthat.rejectedCallbacks.map(cb => cb(reason))
Will execute the callback method we saved to implementPromise.then()
The function.
If the partner is still not clear, you can type the code twice more. If the partner is not interested in jsliang’s understanding, you can look at the Promise A+ specification in the reference, which is more conducive to the understanding of the partner.
Four written Promise
Code source industry poly that article:
At first, I thought the code was sound, but I was too embarrassed to let Jsliang write it silently, so I used the code from the previous chapter.
/ * * *@name JsliangPromise
* @description Handwritten Promise * /
/ * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --, set the share function -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /
// check whether it is a function
const isFunction = (obj) = > {
return typeof obj === 'function';
};
// Determine whether it is an object
const isObject = (obj) = > {
return!!!!! (obj &&typeof obj === 'object');
};
// Check whether it is thenable
const isThenable = (obj) = > {
return (
isFunction(obj)
|| isObject(obj)
) && 'then' in obj;
};
// Check whether this is a Promise
const isPromise = (promise) = > {
return promise instanceof JsliangPromise;
};
/ * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --, set the state of Promise -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /
/** 1. Promise has 3 states: pending, fulfilled, fulfilled * pending: Promise can switch to fulfilled or fulfilled * fulfilled: There must be an immutable value */ in the rejected state and there must be an immutable reason */
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
/ * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - Promise supplement process -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /
// 3.6 handleCallback () According to the state, judge whether this path is a pity path or rejected path
const handleCallback = (callback, state, result) = > {
/ / 3.6.1 deconstruction
const { onFulfilled, onRejected, resolve, reject } = callback;
/ / 3.6.2 judgment
try {
// 3.6.3 If successful
if (state === FULFILLED) {
3.6.4 Judge whether ondepressing is a function
if (isFunction(onFulfilled)) {
// 3.6.5 If so, make its return value the result of the next Promise
resolve(onFulfilled(result));
} else {
3.6.6 If not, use the result of the current Promise as the result of the next Promiseresolve(result); }}else if (state === REJECTED) {
if (isFunction(onRejected)) {
resolve(onRejected(result));
} else{ reject(result); }}}catch (error) {
// 3.6.7 This error is used as the rejected Reason of the next Promisereject(error); }};2.3.4 Clear the previous content
const handleCallbacks = (callbacks, state, result) = > {
while(callbacks.length) { handleCallback(callbacks.shift(), state, result); }};// 2.3 Once the state is not pending, it cannot be converted again
const transition = (promise, state, result) = > {
// 2.3.1 If pending, the corresponding state and result should be set
if(promise.state ! == PENDING) {return;
}
// 2.3.2 If not, set it
promise.state = state;
promise.result = result;
2.3.3 Asynchronously clear all callbacks when the status changes
setTimeout(() = > {
handleCallbacks(promise.callbacks, state, result);
}, 0);
}
2.6 If some special values are resolved, special operations must be performed.
// This special treatment is also explicitly described in the specification
const resolvePromise = (promise, result, resolve, reject) = > {
// 2.6.1 If result is the current Promise itself, TypeError is raised
if (result === promise) {
return reject(new TypeError('Can not fulfill promise with itself'));
}
2.6.2 If result is another Promise, the current state and result state are used
if (isPromise(result)) {
return result.then(resolve, reject);
}
// 2.6.3 If result is a thenable object
// Call The then function to re-enter The Promise Resolution procedure
if (isThenable(result)) {
try {
if (isFunction(result.then)) {
return newJsliangPromise(then.bind(result)).then(resolve, reject); }}catch (error) {
returnreject(error); }}// 2.6.4 If no, directly resolve result
resolve(result);
};
/ * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --, Promise to realize -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /
// 2. Set Promise
const JsliangPromise = function(f) {
// 2.1 Setting the initialization status
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 = [];
// this is a big pity; // this is a big pity; // this is a big pity
const onFulfilled = value= > transition(this, FULFILLED, value);
const onRejected = reason= > transition(this, REJECTED, reason);
// 2.3 Use ignore to ensure that resolve/reject is called only once
let ignore = false;
// 2.4 Set the resolve processing mode
let resolve = (value) = > {
if (ignore) {
return;
}
ignore = true;
// 2.5 Determine three rules for resolve
resolvePromise(this, value, onFulfilled, onRejected);
};
let reject = (reason) = > {
if (ignore) {
return;
}
ignore = true;
onRejected(reason);
}
// 2.6 Try
try {
// 2.6.1 Pass resolve and reject as arguments to f for easy call
f(resolve, reject);
} catch (error) {
If f fails, reject (reject) is used as a reasonreject(error); }}// 3. Promise.then
JsliangPromise.prototype.then = function (onFulfilled, onRejected) {
The 3.2. then() method returns a Promise, so you need to return one
return new JsliangPromise((resolve, reject) = > {
// 3.3 Set callback
const callback = { onFulfilled, onRejected, resolve, reject };
3.4 If state is pending, store it in the Callbacks list
if (this.state === PENDING) {
this.callbacks.push(callback);
} else {
If not, throw a handleCallback
// Why use setTimeout? Since we can't simulate microtasks, let's do macro tasks instead
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);
});
Copy the code
Five Promise. All ()
Now let’s practice some questions.
Given 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);
});
Copy the code
Required output: [500, 1000].
Promise.myAll = function(arr) {
// 1. Return a 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. Go through the list
for (let i = 0; i < arr.length; i++) {
// 5 set the content for result in. Then
// If index ends, resolve(result)
/ / otherwise reject (err)
arr[i].then((res) = > {
result[i] = res;
index++;
if (index === length) {
resolve(result);
}
}).catch((err) = > {
throw new Error(err); })}})};Copy the code
Six Promise. Race ()
Given 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);
});
Copy the code
Required output: 500.
The race method is similar to the previous all method, except that it returns the result the first time it executes.then, rather than 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);
})
Copy the code
Promise asynchronous scheduler
Review the problem and complete the following code:
** The Scheduler class in the following code is improved so that the program can 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
// At the beginning, tasks 1 and 2 are queued
// At 500ms, complete 2, output 2, task 3 enter the queue
// At 800ms, complete 3, output 3, task 4 enter the queue
// At 1000ms, complete 1, output 1, no next queue
// at 1200ms, complete 4, output 4, no next queue
// Queue completion, output 2 3 1 4
Copy the code
Implementation (async/await) :
** The Scheduler class in the following code is improved so that the program can output */ correctly
class Scheduler {
constructor(maxNum) {
this.taskList = [];
this.count = 0;
this.maxNum = maxNum; // Maximum number of concurrent requests
}
async add(promiseCreator) {
// If the current concurrency exceeds the maximum concurrency, the task queue is entered
if (this.count >= this.maxNum) {
await new Promise((resolve) = > {
this.taskList.push(resolve); })}// The number of times + 1 (if the previous execution is not complete, keep adding)
this.count++;
// Wait for the contents to complete
const result = await promiseCreator();
// Number of times -1
this.count--;
// The first team to leave the team
if (this.taskList.length) {
this.taskList.shift()();
}
// return the result value
returnresult; }}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
// At the beginning, tasks 1 and 2 are queued
// At 500ms, complete 2, output 2, task 3 enter the queue
// At 800ms, complete 3, output 3, task 4 enter the queue
// At 1000ms, complete 1, output 1, no next queue
// at 1200ms, complete 4, output 4, no next queue
// Queue completion, output 2 3 1 4
Copy the code
Viii References
- Promises/A+ 100 lines of code[Recommended Reading: 30min]
- Implement Promises with minimal support for asynchronous chained calls (20 lines)【 Recommended reading: 20min】
- Classic INTERVIEW Questions: The most detailed handwritten Promise tutorial ever[Recommended Reading: 30min]
- 1. Write A Promise that Promises/A+ Promises from scratch[Reading advice: Probably read it all over, no clear analysis of the previous]
- Promise implementation principle (source code attached)[Reading advice: Probably read it all over, no clear analysis of the previous]
- Analyze the internal structure of Promise, step by step to achieve a complete Promise class that can pass all Test cases[Recommended reading: write in detail, not clear analysis of the previous]
- Xiao Shao teach you to play promise source code[Recommended reading: write in detail, not clear analysis of the previous]
- Promise won’t…? Look here!! The most accessible Promise ever!![Recommended reading: write in detail, not clear analysis of the previous]
Jsliang’s document library is licensed by Junrong Liang under the Creative Commons Attribution – Non-commercial – Share alike 4.0 International License. Based on the github.com/LiangJunron… On the creation of works. Outside of this license agreement authorized access can be from creativecommons.org/licenses/by… Obtained.