Home  >  Article  >  Web Front-end  >  JavaScript Study Guide: Callback Issues_Basic Knowledge

JavaScript Study Guide: Callback Issues_Basic Knowledge

WBOY
WBOYOriginal
2016-05-16 15:04:191026browse

Callback Hell

For JavaScript programmers, handling callbacks is commonplace, but dealing with callbacks that are too deep is not so beautiful. The following sample code snippet uses three layers of callbacks. Let's think about more layers of scenarios. It's simply It's sour, 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 deal with the problem of callback hell, such as bluebird, Q, etc. This article focuses on the support for asynchronous programming in the ECMAScript 6/7 specification.

ES6 Promise

Promise is a solution to asynchronous programming and a powerful tool to solve the problem of callback hell.

Promise was accepted by the mainstream in the JavaScript ecosystem in 2007 when the Dojo framework added the dojo.Deferred function. With the popularity of dojo.Deferred, in 2009 Kris Zyp proposed the CommonJS Promises/A specification. Subsequently, a large number of Promise implementations appeared in the ecosystem, including Q.js, FuturesJS, etc. Of course, the popularity of Promise is largely due to the existence of jQuery, but jQuery does not fully comply with the CommonJS Promises/A specification. Then as you can see, the ES 6 specification includes Promises.
MDN describes Promise like this:

The Promise object is a proxy that returns a value. This return value may not be known when the promise object is created. It allows you to specify handling methods for the success or failure of asynchronous operations. This allows asynchronous methods to return values ​​just like synchronous methods: asynchronous methods will return a
containing the original return value. The following code is the example in the "Callback Hell" section implemented through Promise. The code does not seem very concise, but it is significantly improved compared to traditional hierarchical callbacks, and the code is more maintainable and readable.

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 implementation of Promise is not simple enough. We also need better options, and co is one of the options. co is an asynchronous flow controller based on Generator. Before understanding co, you first need to understand Generator. Students who are familiar with C# should know that C# version 2.0 introduced the yield keyword for iterative generators. ES 6 Generator is similar to C#. It also uses the yield syntax sugar and implements a state machine internally. For specific usage, please refer to the function* section of the MDN documentation. For principles, please refer to the AlloyTeam team blog for an in-depth understanding of Generator. Use co to cleverly combine 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 to the following example. The general idea is to use recursive traversal of the generator until the state is completed. Of course, co does more.

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 indeed very good, but unfortunately 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 calls. Well, .net is one step ahead, .net framework 4.5 has taken the lead in supporting it.
The code to be written in the future will look like this:

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 classic callback asynchronous programming method, to the improvement of asynchronous programming in the ES6 Promise specification, to the elegant processing of co combined with ES Generator, and finally the perfect ending of ES7 async/await, which allows us to understand why these features appear in ECMAScript And what problems were solved, and we can see more clearly the development trend of JavaScript asynchronous programming.

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn