JavaScript asynchronous programming and javascript asynchronous programming
With the development of the front-end, the word Asynchronous is becoming more and more common. Suppose we have such an asynchronous task:
Initiate several requests to the server. The result of each request is used as the parameter of the next request.
Let's take a look at our solutions:
Callbacks
The first and most commonly used callback function is the callback function. Let's perform simple encapsulation:
let makeAjaxCall = (url, cb) => { // do some ajax // callback with result}makeAjaxCall('http://url1', (result) => { result = JSON.parse(result)})
Well, it looks good! But when we try to nest multiple tasks, the code looks like this:
makeAjaxCall('http://url1', (result) => { result = JSON.parse(result) makeAjaxCall(`http://url2?q=${result.query}`, (result) => { result = JSON.parse(result) makeAjaxCall(`http://url3?q=${result.query}`, (result) => { // ... }) })})
Oh, my God! Let the heap go.}) Hell!
So we want to tryJavaScript event model:
1. Pub/Sub
In DOM event processing, Pub/Sub is a common mechanism. For example, we need to add event listening to elements:
elem.addEventListener(type, (evt) => { // handler})
So can we also construct a similar model to process asynchronous tasks?
First, build a distribution center and add the on/emit method:
let PubSub = { events: {}, on(type, handler) { let events = this.events events[type] = events[type] || [] events[type].push(handler) }, emit(type, ...datas) { let events = this.events if (!events[type]) { return } events[type].forEach((handler) => handler(...datas)) }}
Then we can use it like this:
const urls = [ 'http://url1', 'http://url2', 'http://url3']let makeAjaxCall = (url) => { // do some ajax PubSub.emit('ajaxEnd', result)}let subscribe = (urls) => { let index = 0 PubSub.on('ajaxEnd', (result) => { result = JSON.parse(result) if (urls[++index]) { makeAjaxCall(`${urls[index]}?q=${result.query}`) } }) makeAjaxCall(urls[0])}
There seems to be no revolutionary change to the callback function, but the advantage is that we can put the request and processing functions in different modules to reduce coupling.
2. Promise
The Promise specification is a revolutionary change. With Promise, we can complete asynchronous tasks like this:
let makeAjaxCall = (url) => { return new Promise((resolve, reject) => { // do some ajax resolve(result) })}makeAjaxCall('http://url1') .then(JSON.parse) .then((result) => makeAjaxCall(`http://url2?q=${result.query}`)) .then(JSON.parse) .then((result) => makeAjaxCall(`http://url3?q=${result.query}`))
Great! It is written like a synchronous function!
Don't worry, boy. We also have better:
3. Generators
Another major killer of ES6 is Generators [2]. In a generator function, we can use yield statements to interrupt function execution and use the next method to iterate statements outside the function, more importantly, we can use the next method to inject data into the function and dynamically change the behavior of the function. For example:
function* gen() { let a = yield 1 let b = yield a * 2 return b}let it = gen()it.next() // output: {value: 1, done: false}it.next(10) // a = 10, output: {value: 20, done: false}it.next(100) // b = 100, output: {value: 100, done: true}
Use generator to encapsulate our previous makeAjaxCall functions:
let makeAjaxCall = (url) => { // do some ajax iterator.next(result)}function* requests() { let result = yield makeAjaxCall('http://url1') result = JSON.parse(result) result = yield makeAjaxCall(`http://url2?q=${result.query}`) result = JSON.parse(result) result = yield makeAjaxCall(`http://url3?q=${result.query}`)}let iterator = requests()iterator.next() // get everything start
Oh! It looks like the logic is clear, but it feels uncomfortable to inject iterator from the outside every time ......
Don't worry. Let's mix Promise and Generator to see what black magic will be produced:
let makeAjaxCall = (url) => { return new Promise((resolve, reject) => { // do some ajax resolve(result) })}let runGen = (gen) => { let it = gen() let continuer = (value, err) => { let ret try { ret = err ? it.throw(err) : it.next(value) } catch (e) { return Promise.reject(e) } if (ret.done) { return ret.value } return Promise .resolve(ret.value) .then(continuer) .catch((e) => continuer(null, e)) } return continuer()}function* requests() { let result = yield makeAjaxCall('http://url1') result = JSON.parse(result) result = yield makeAjaxCall(`http://url2?q=${result.query}`) result = JSON.parse(result) result = yield makeAjaxCall(`http://url3?q=${result.query}`)}runGen(requests)
The runGen function looks like an automatic machine!
In fact, the runGen method is an implementation of ECMAScript 7 async function:
4. async function
ES7 introduces a more natural feature async function [3]. With the async function, we can complete the task as follows:
let makeAjaxCall = (url) => { return new Promise((resolve, reject) => { // do some ajax resolve(result) })};(async () => { let result = await makeAjaxCall('http://url1') result = JSON.parse(result) result = await makeAjaxCall(`http://url2?q=${result.query}`) result = JSON.parse(result) result = await makeAjaxCall(`http://url3?q=${result.query}`)})()
Just like when we combine Promise and Generator in the above section, the await keyword also accepts a Promise. In async function, the remaining statements are executed only after the await statement is completed. The whole process is like encapsulating Generator with runGen function.
The above are some JavaScript asynchronous programming modes summarized in this article. I hope this will be helpful for your learning.
Articles you may be interested in:
- Javascript asynchronous programming
- Four Asynchronous Javascript programming methods allow you to write better programs
- Six features of the Promise mode in JavaScript asynchronous programming
- Detailed introduction to the Javascript asynchronous programming model Promise Mode
- Detailed introduction to asynchronous programming specification Promises/A in Javascript
- Nodejs asynchronous programming
- How to use javascript to solve asynchronous programming exceptions
- Understanding javascript asynchronous programming