A callback function is considered as an advanced function, and an advanced function that is passed as a parameter to another function (called & quot; otherFunction & quot, the callback function is called (or executed) in otherFunction ). The essence of a callback function is a mode (a mode to solve common problems). Therefore, a callback function is also called a callback mode.
Callback hell
For JavaScript programmers, callback processing is a common practice, but a callback with too many processing layers is not that good. The following sample code snippet uses a layer-3 callback, and then makes up the brain for more multi-layer scenarios, it's sour, and this is the legendary callback hell.
getDirectories(function(dirs) { getFiles(dirs[0], function(files) { getContent(files[0], function(file, content) { console.log('filename:', file); console.log(content); }); });}); function getDirectories(callback) { setTimeout(function() { callback(['/home/ben']); }, 1000);} function getFiles(dir, callback) { setTimeout(function() { callback([dir + '/test1.txt', dir + '/test2.txt']); }, 1000)} function getContent(file, callback) { setTimeout(function() { callback(file, 'content'); }, 1000)}
Solution
There are many asynchronous solutions in the ecosystem that can handle callback hell problems, such as bluebird and Q. This article focuses on the support for asynchronous programming in ECMAScript 6/7 specifications.
ES6 Promise
Promise is an asynchronous programming solution and a powerful tool to solve the callback hell problem.
Promise was widely accepted in the JavaScript ecosystem. In 2007, the Dojo framework added the dojo. Deferred function. With the popularity of dojo. Deferred, Kris Zyp proposed the CommonJS Promises/A specification in 2009. Subsequently, a large number of Promise implementations emerged in the ecosystem, including Q. js and FuturesJS. Of course, the popularity of Promise is largely due to the existence of jQuery, but jQuery does not fully comply with CommonJS Promises/A specifications. As you can see later, the ES 6 Specification includes Promise.
The Promise in MDN is described as follows:
The Promise object is a proxy of the returned value. The returned value may not be known when the promise object is created. It allows you to specify a processing method for successful or failed asynchronous operations. This allows the Asynchronous Method to return values like the synchronous method: The Asynchronous Method returns
The following code is implemented using Promise as an example in the "Callback hell" section. It seems that the Code is not very concise, but it is significantly improved compared with the traditional hierarchical callback, and the code maintainability and readability are stronger.
getDirectories().then(function(dirs) { return getFiles(dirs[0]);}).then(function(files) { return getContent(files[0]);}).then(function(val) { console.log('filename:', val.file); console.log(val.content);}); function getDirectories() { return new Promise(function (resolve, reject) { setTimeout(function() { resolve(['/home/ben']); }, 1000); });} function getFiles(dir) { return new Promise(function (resolve, reject) { setTimeout(function() { resolve([dir + '/test1.txt', dir + '/test2.txt']); }, 1000); });} function getContent(file) { return new Promise(function (resolve, reject) { setTimeout(function() { resolve({file: file, content: 'content'}); }, 1000); });}
ES6 Generator
The Promise implementation method is not concise enough. We still need a better choice. co is one of the choices. Co is an asynchronous stream Controller Based on Generator (Generator). Before learning about co, you must first understand Generator. Everyone familiar with C # should be familiar with it. In C #2.0, the yield keyword is introduced for iteration generators. Similar to C #, ES 6 Generator also uses the yield syntax sugar to internally implement the state machine. For detailed usage, refer to function * in the MDN document. For more information about the principle, see the Blog of AlloyTeam. Use co cleverly combined with ES6 Generator and ES6 Promise to make asynchronous calls more harmonious.
co(function* (){ var dirs = yield getDirectories(); var files = yield getFiles(dirs[0]); var contentVal = yield getContent(files[0]); console.log('filename:', contentVal.file); console.log(contentVal.content);});
Co is very clever. Its core code can be simplified as follows. The general idea is to use a recursive traversal generator until the state is complete. Of course, co does a lot of work.
runGenerator(); function* run(){ var dirs = yield getDirectories(); var files = yield getFiles(dirs[0]); var contentVal = yield getContent(files[0]); console.log('filename:', contentVal.file); console.log(contentVal.content);} function runGenerator(){ var gen = run(); function go(result){ if(result.done) return; result.value.then(function(r){ go(gen.next(r)); }); } go(gen.next());}
ES7 Async/Await
ES6 Generator is really good, but it is a pity that it requires the support of third-party libraries. The good news is that ES 7 will introduce the Async/Await keyword to perfectly solve the problem of asynchronous calling. Well,. net is one step ahead, and. net framework 4.5 is already supported first.
The code for the future is written as follows:
run();async function run() { var dirs = await getDirectories(); var files = await getFiles(dirs[0]); var contentVal = await getContent(files[0]); console.log('filename:', contentVal.file); console.log(contentVal.content);}
Conclusion
From the asynchronous programming method of the classic callback, to the improvement of the asynchronous programming by ES6 Promise specification, to the cococoworks with ES Generator for elegant processing, ES7 async/await is final, we can understand why ECMAScript has these features and what problems it has solved, and see the development of JavaScript asynchronous programming more clearly.