This article describes the pause and recovery of javascript functions. Share it for your reference, as follows:
Asynchronous programming of javascript has always been a difficult problem. At first we used callback, but then triggered the callback hell, so we "invented" Promise to solve the problem of callback being too deeply nested. However, due to the abuse of Promise (a series of then), the code becomes difficult to read. At this time, async-await came out of nowhere, which allowed us to write asynchronous code in a synchronous way, which was so amazing that some people said it was a silver bullet for asynchronous programming of javascript.
. Code is just a demonstration and is not available
function getProfile(id) { return (`/wedding/profile/${weddingId}` } async function getWeddingDetail(weddingId) { try { // Pause execution const wedding = await (`/wedding/${weddingId}`); // When the result returns to resume execution, then continue to pause const groom = await getProfile(); // ... Resuming execution -> Pause ... const bride = await getProfile(); // ... Resume execution return { wedding, bride, groom }; } catch (error) { handleError(error); } }
No silver bullet
However, silver bullets do not exist in the field of computer science. async-await also has its shortcomings, such as if you forgot to write await, don’t you believe it?
Suppose someone else wrote a tool function called getProfile. If you don’t understand its specific implementation, do you just treat it as a synchronous function, even if getProfile is asynchronous.
Of course, this is just a small problem. What makes me even more uncomfortable is that if you use async in a function, the function that calls it must also become an async. If there is another function that needs to call this calling function...holly shit! Now you understand.
Is there a way to get the best of both worlds?
// getWeddingDetail doesn't have to worry about whether the internal function is asynchronous or synchronousfunction getWeddingDetail(weddingId) { const wedding = (`/wedding/${weddingId}`); const groom = getProfile(); const bride = getProfile(); return { wedding, bride, groom }; }
Nothing cannot be solved by an intermediate layer
The core of asynchronous programming is to pause and resume execution of functions. Deciding whether a function is suspended or resumed execution is the work done by js runtime. Could it be that we are going to go deep into the engine implementation today?
No! I don't understand C++, nor how the js engine is implemented.
However, I can write an intermediate layer (function runtime) to try to implement the above requirements, of course, there will be some limitations.
1. Entry function
Assume that the function to be run is as follows:
function main() { const id = 123; ('Getting wedding:', id); const { wedding, bride, groom } = getWeddingDetail(id); ('Wedding detail:', wedding); }
We expect to be able to operate as follows:
function runtime(mainFn) { mainFn(); } // start runtime runtime(main);
The basic framework is already available, what to do next?
First, we need to figure out how to interrupt function operation without using await.
Then, resume execution where appropriate.
There are two ways to interrupt function running in js: return and throw. I choose throw because it means an interrupt caused by an exception encountered. OK, let's change the runtime
function runtime(mainFn) { const _originalFetch = ; = (url, options) => { // "pause" throw new Error(); }; // Run the entry function runMain(); function runMain() { try { mainFn(); } catch (error) { // Function "pause" // Restore and re-execute mainFn runMain(); } } }
First, ignore the problems that occur in this code and focus on the two points of function "interrupt" and "recovery". Obviously, the purpose has been achieved. Next, optimize it.
The first thing to be affected is runMain, which only needs to be executed after success:
function runtime(mainFn) { const _originalFetch = = (url, options) => { _originalFetch(url, options).then(res => { //Resume execution after returning the result runMain() }) throw new Error() } runMain(); function runMain() { try { mainFn(); } catch (error) { // ignore } } }
Each time an exception is thrown, this causes the execution of mainFn infinite loop.
To solve this problem, we need to introduce a cache so that we only need to throw an exception on the first fetch and return a response for the subsequent request.
function runtime(mainFn) { const _originalFetch = = (url, options) => { if (([url, options])) return ([url, options]) _originalFetch(url, options).then(res => { ([url, options], res) runMain() }) throw new Error() } runMain(); function runMain() { try { mainFn(); } catch (error) { // ignore } } }
Success!
Run the program and check the output of console. Since it has been repeated many times,'Getting wedding:', 123
It has also been shown several times, which is caused by side effects.
2. Pure functions
runtime only allows running pure functions, if there are side effects in your code, you must add a restriction: runSideEffects().
function main() { const id = 123; runSideEffects(() => ('Getting wedding:', id)); const { wedding, bride, groom } = getWeddingDetail(id); runSideEffects(() => ('Wedding detail:', wedding)); }
The implementation of sideEffects is very easy:
function runtime(mainFn) { //Refer to the above code // Provide `runSideEffects` const sideEffects = []; = fn => { (fn); }; runMain(); function runMain() { try { mainFn(); (fn => fn()); } catch (error) { // Clear side effects (0, ); } } }
Run again,'Getting wedding:', 123
Show only once ~
What did you do?
To mimic the function pause and restore, we "pause" the function by throwing an error and rerunning it to "recover" the function.
In order to "recover" from the pause, we need to replace the thrown error with the function return value, and we use the cache mechanism to achieve this goal.
Finally, in order to safely repeat the execution of the function, it needs to be converted into a pure function. If there are side effects, collect them and then execute the side effects after the function runs successfully.
So much, what are the practical uses?
This article is inspired byReact Suspense. With Suspense, you can get data like this:
function Component() { const data = getDataFromNetwork(); return <div />; }
getDataFromNetwork will initiate an asynchronous request, so it is an asynchronous function, but React makes it appear to be a synchronous operation. This is very interesting ~
Original reading:pause-and-resume-a-javascript-function
Interested friends can use itOnline HTML/CSS/JavaScript code running tool:http://tools./code/HtmlJsRunTest the above code running effect.
For more information about JavaScript, please view the special topic of this site: "Summary of common JavaScript functions techniques》、《JavaScript object-oriented tutorial》、《Summary of JavaScript Errors and Debugging Skills》、《Summary of JavaScript data structure and algorithm techniques"and"Summary of JavaScript mathematical operations usage》
I hope this article will be helpful to everyone's JavaScript programming.