origin
Small fly is a new person who just entered the front end of the line soon, because into a big company, like to become the eyes of the younger brother and younger sister ‘god’, we encounter JS problems like to ask him, this is not, his QQ pop up such a message
“Hi, is god there? I have a question, right now we have something like this in our code, but it doesn’t return the correct result
function getDataByAjax () { return $.ajax(... postParam) } var data = getDataByAjax() if (data) { console.log(data.info) }Copy the code
“Oh, you have an asynchronous call here, you can’t get the return value directly, you have to write the if statement in the callback function,” Xiao Fei said without thinking, for a ‘professional’ FE, this is not a problem. “But I want to just change the getDataByAjax method and make the rest of the code work.” Async: True async: True async: True Async: True Async: True Async: True async: True async: True async: True
Two days later, she posted on QQ with a sad face. “I tried your method, but it didn’t work at all, cry ~~” “Don’t worry, LET me check your postParam parameters.”
{... dataType: 'jsonp', async: true ... }Copy the code
“This is a JSONP request, this is a jSONP request, this is a jSONP request, there is no way to synchronize jSONP requests,” I know that jSONP requests are implemented through the script tag, but script also supports synchronization, You see www.w3school.com.cn/tags/attscr…” I’m a little surprised that jquery is not implemented, but since the w3School standard is there, it doesn’t matter if you have two lines of code.
export const loadJsonpSync = (url) => { var result; window.callback1 = (data) => (result = data) let head = window.document.getElementsByTagName('head')[0] let js = window.document.createElement('script') js.setAttribute('type', 'text/javascript') js.setAttribute('async', Js.setattribute (' SRC ', URL) head.appendChild(js) return result}Copy the code
Well, the result is undefined! W3cshool document should not be allowed, but also the authority, I don’t see how, small fly secretly thought. “I just tried it, there is something wrong with the w3School documentation, the asynchronous property is completely wrong” “But I just tried it once, I am sure it is ok.”
<script src="loop50000 && put('frist').js"></script>
<script src="put('second').js"></script>
Copy the code
(Interested students can implement the following two JS and try it with the async tag.) “This, I don’t know”, small fly shan shan of say to the other party already offline
abstract
About this question, believe not only xiaofei, many people are difficult to answer. Why ajax can synchronize but jSONp can’t, generalize to NodeJS, and why readFile can synchronize but some libraries can’t. (The script async option is left out because the knowledge dimension is not enough, but don’t worry, it will be explained below.) For now, let’s abstract the problem from a computer science perspective:
Can we convert asynchronous code to synchronous code? (ASYNCCALL => SYNCCALL)
Since the problem is abstract, we can look at it not from an engineering/performance/implementation language perspective (synchronous is less efficient than asynchronous), but with each additional dimension the complexity explodes geometrically.
First, let’s clarify the definition of == synchronous and asynchronous in computer science
Synchronization refers to the coordination of events occurring in a system to achieve consistency and unification in time. Synchronizing in the system is also known as in time, synchronous, in sync. — from Baidu Baike
Asynchrony is the opposite of synchronization. That is, time is not consistent, not unified
With this in mind, we can use gantt charts to represent synchronous and asynchronous
Where T1 and T2 are synchronous, t1 and T3 are asynchronous. The answer is in the principles of operating systems textbook, we have spin locks, semaphores to solve the problem, pseudo code below
SpinLock () {fork Wait 3000 unlock() Put 'unlock'} // PV primitive, execute the next step if the semaphore is false, and set the semaphore to true // Otherwise suspend the current execution stack, Semaphore () {pv() fork Wait 3000 uv() pv() uv() Put 'unlock'}Copy the code
Very good, so far can be in the operating system principles textbook to turn over the answer. So we add constraints on top of that
Can we convert asynchronous code to synchronous code just by relying on JS itself? (ASYNCCALL => SYNCCALL)
argument
With that in mind, let’s take a look at the jquery source at github.com/jquery/jque… As you can see, the ajax synchronization mechanism is essentially implemented by XMLHttpRequest, not js native. In the same vein, take a look at the nodejs source github.com/nodejs/node… Follow readFileSync->tryReadSync->readSync all the way to a c++ binding, github.com/nodejs/node…
if (req->IsObject()) {
ASYNC_CALL(read, req, UTF8, fd, &uvbuf, 1, pos);
} else {
SYNC_CALL(read, 0, fd, &uvbuf, 1, pos)
args.GetReturnValue().Set(SYNC_RESULT);
}
Copy the code
The secret of synchronization lies in the c++ macro definition, which is a low-level way of synchronization implemented by c++. Looking at the two most widely used asynchronous to synchronous call-outs, we find that neither is implemented using JS. Seems to be from the phenomenon level js can’t native support, but it is not enough, we explore under the js semantic characteristics of the spin lock/semaphore simulation implementation (and I know you will, = = js itself is single-threaded, only simulate the characteristics of the multithreaded = = I very agree with this sentence, so there is not implemented, In addition, since setTimeout has similar asynchronous execution properties to fork, we temporarily replace fork with Setitmeout
spinlocks
1. First implementation version
var lock = true
setTimeout(function () {
lock = false
}, 5000)
while(lock);
console.log('unlock')
Copy the code
We expected to execute unlock after 5000ms, but tragically, the entire Chrome process froze. In order to explain the problem, we read nguyen other teacher’s event loop model www.ruanyifeng.com/blog/2014/1… It seems that we have a clear understanding of the essence of eventloop, the JS running sequence (synchronous executing code immediately, asynchronous code waiting queue), so we can give js VM scheduling implementation based on this (an implementation of eventLoop), of course, we only need to simulate asynchronous operation in order to explain the spin lock failure. Synchronous operations, and loops
//taskQueue: taskQueue //runPart: task currently being performed (synchronize instruction set) //instruct: Function eventloop (taskQueue) {while(runPart = taskqueue.shift ()) {while(instruct = runpart.shift ()) {const { type, act, codePart } = instruct switch(type) { case 'SYNC': console.log(act) if (act === 'loop') runPart.unshift({ act: 'loop', type: 'SYNC' }) break case 'ASYNC': taskQueue.push(codePart) break } } } }Copy the code
Then transform our first version of the spin lock
let taskQueue = [
[
{act: 'var lock = true', type: 'SYNC'}, //var lock = true
{
act: 'setTimeout',
type: 'ASYNC',
codePart: [
{act: 'lock = false', type: 'SYNC'}
]
}, // setTimeout(function () { lock = false }, 5000)
/*{
act: 'loop',
type: 'SYNC'
},*/ // while(lock);
{
act: 'console.log(\'sync\')',
type: 'SYNC'
} // console.log('unlock')
]
]
Copy the code
Test evnet loop, then release comment, we successfully loop block the entire execution, lock = false never execute!! (The real scheduling mechanism is much more complex than this, interested can see the implementation of Webkit ~~~ jScore.)
Knowing the principle, we will manually improve this part of the code 2. Improved code
var lock = true
setTimeout(function () {
lock = false
console.log('unlock')
}, 5000)
function sleep() {
var i = 5000
while(i--);
}
var foo = () => setTimeout(function () {
sleep()
lock && foo()
})
foo()
Copy the code
This version improves our performance on while(true); In fact, this technique is widely used to improve the page experience, so some people are wrong to resist using setTimeout because the timing is unpredictable! Blog.csdn.net/kongls08/ar… .
Quiz 1: Rewrite EventLoop and taskQueue to support the improved code
However, if we change foo() at the end of the code to foo() && console.log(‘wait5sdo’), our code still fails, why
Notice where we’ve marked it in red, if you complete quiz 1, you’ll get the same order as this picture
== Synchronously executed snippets must precede asynchronously executed snippets. = =
So, no matter from theory or practice, we have to admit, in JS, the asynchronous method into synchronous method this proposition is water moon mirror flower
Document. appendChild is a dom render thread. Async blocks dom parsing. Instead of blocking the JS engine. In fact, after async acquires resources, the interaction with the JS engine is still the action of push taskQueue, which is called async Call
For details on DOM parsing, see WebKit Technology Insider chapter 9, Resource Loading
To master the
I’m sure many of you are already using the async/await syntax. In the syntax below, getAjax1 and Console have synchronization features
async function () {
var data = await getAjax1()
console.log(data)
}
Copy the code
Having covered the nature of Event loops and asynchrony, let’s revisit async/await. Boy, this code personally overturned == synchronously executed code snippets must precede asynchrony. == the golden rule! Surprise or no surprise, it’s in our model like a proton in a trisolan. We looked at the above model again and couldn’t find any bugs, couldn’t find anything to override, so I really have to admit that async/await is definitely a super magic trick. From the perspective of async/await itself, I believe many people will say that async/await is the syntactic sugar of CO, and CO is the syntactic sugar of generator/promise. Ok, then we may as well remove this syntactic sugar. To see the nature of the code, about the CO, there are too many people in the reading, I am really not a cliche, you can see this article, we’ll directly around the past, here is the realization of a simple www.cnblogs.com/jiasm/p/580…
function wrap(wait) {
var iter
iter = wait()
const f = () => {
const { value } = iter.next()
value && value.then(f)
}
f()
}
function *wait() {
var p = () => new Promise(resolve => {
setTimeout(() => resolve(), 3000)
})
yield p()
console.log('unlock1')
yield p()
console.log('unlock2')
console.log('it\'s sync!!')
}
Copy the code
Finally, we get to the crux of the matter, and if we just look at the Wait generator (not just any function, mind you), it looks familiar. This is the spinlock pseudocode we came up with in the first place!! This has been completely rejected by us. Js spin-locks are not possible, and if things go wrong, there must be a yield and * that perform async/await magic. Generator and yield are literal meanings. A Gennerator is called a generator, and the yield part is controversial in ruby, Python, JS and other languages, but most people agree with the concept of “concession” (I have seen the debate on Maillist before, but I can’t find the specific content).
Extended Reading – Ruby Metaprogramming Closure chapter Yield (Yield under Ruby Semantics)
Concessionary means that the CPU cedes the right to use it during execution. From an operating system perspective, this is a ‘suspend’ primitive that, in the semantics of eventLoop, seems to hold the block of code that is currently being executed (in our eventLoop case, the runPart) and then execute the next block sequentially. Eventloop can be modified to implement the concession mechanism
Quiz 2 Modify eventLoop to support yield primitives
At this point, you can fix the problem by modifying the EventLoop model, but that’s not magic.
A world of harmonious coexistence
In fact, with Babel, we can easily degrade the use of yield (in the es5 world, use the concept of yield!!). What seems impossible, now, let’s pick up once argued that == synchronously executed code snippets must precede asynchrony. == this theorem, on the basis of the inverse transformation
== Code that follows asynchronous code execution must not be executed synchronously (asynchronous). = =
Now let’s allow callback to perform the same function as a wait generator without using generator/yield!!
function wait() { const p = () => ({value: new Promise(resolve => setTimeout(() => resolve(), 3000))}) let state = { next: () => { state.next = programPart return p() } } function programPart() { console.log('unlocked1') state.next = programPart2 return p() } function programPart2() { console.log('unlocked2') console.log('it\'s sync!! ') return {value: void 0} } return state }Copy the code
Great, we have successfully converted generator to function (albeit at a high cost), and the code itself illustrates the nature of generators, high-order functions, fragment generators, or function generators. This is exactly the same as the translation on SCIP and has its own state (finite state machine)
I recommend reading the Generator section of Chapter 1, Construction and Interpretation of computer programs
Quiz 3 The solution we provide is actually flawed. Please talk about it from a scope perspective
In fact, unconsciously, we have to reinvent the well-known en.wikipedia.org/wiki/Contin CPS transformation in computer science…
Finally, let me introduce you to Facebook’s CPS automatic transformation tool, ReGenerator. He corrected the scoping bug on our basis, making the Generator natural and elegant in the es5 world. Hats off to Facebook!! Github.com/facebook/re…
Afterword.
Synchronous asynchronous Was the circle inside the most like to talk about problems, however, talking, seems to be most commonly known as’ into the so-called ‘agreement, we mean the pursuit of new technology at the same time, is not concerned about how the new technology and old technology on inheritance, in the development of learning and I do not know why, conformity of specious js.
Technology should not be impetuous
PS: The biggest credit is not CO, nor Babel. Regenerator appeared a few months earlier than Babel, and the initial implementation was based on Esprima/Recast. Not much was known about Resprima/Recast in The country. The Esprima/Esprima-Fb/Acron and Recast /jstransfrom/babel-generator families fought a fierce battle around react, and perhaps one day, I’ll talk a little bit more about implementation details why Babel has the last laugh ~~~~