JS 的异步编程可以说在日常的前端业务开发中经常出现:回调函数、事件监听、Promise、Generator、async/await。

问题1:

同步编程和异步编程的区别在哪里?

问题2:

回调地狱有哪些方法可以解决?

同步和异步

什么是同步?

同步就是在执行某代码时,在该代码没有得到返回结果之前,其他代码暂时是无法执行的,但是一旦执行完成拿到返回值之后,就可以执行其他代码了

什么是异步?

异步就是当某一代码执行异步过程调用发出后,这段代码不会立刻得到返回结果。而是在异步调用发出之后,一般通过回调函数处理这个调用之后拿到结果

JS 编程中为什么需要异步?

JavaScript 是单线程的,如果JS都是同步代码执行可能会造成阻塞

如果使用异步则不会阻塞,我们不需要等待异步代码执行的返回结果,可以继续执行异步任务之后的代码逻辑

JS 异步编程方式发展历程

按照时间顺序来看下 JS 异步编程的实现方式

回调函数

比如比较经典的事件的回调,但是使用回调函数来实现存在一个很常见的问题,那就是回调地狱

fs.readFile(A, 'utf-8', function(error, data){
    fs.readFile(B, 'utf-8', function(error, data){
        fs.readFile(C, 'utf-8', function(error, data) {
            fs.readFile(D, 'utf-8', function(error, data){
                // ...
            });
        });
    });
});
  1. ajax 请求的回调
  2. 定时器中的回调
  3. 事件回调
  4. Node.js 中的一些方法回调

异步回调如果层级很少,可读性和代码的维护性暂时还是可接受的,一旦层级变多就会陷入回调地狱

Promise

提出了 Promise 的解决方案,ES6 又将其写进了语言标准,采用Promise 的实现方式一定程度上解决了回调地狱的问题

function read(url) {
    return new Promise((resolve, reject) => {
        fs.readFile(url, 'utf8', (err, data) => {
            if (err) reject(err);
            resolve(data);
        });
    });
}
read(A).then(data => {
    return read(B);
}).then(data => {
    return read(C);
}).then(data => {
    return read(D);
}).catch(reason => {
    console.log(reason);
});
function read(url) {
    return new Promise((resolve, reject) => {
        fs.readFile(url, 'utf8', (err, data) => {
            if (err) reject(err);
            resolve(data);
        });
    });
}
// 通过 Promise.all 可以实现多个异步并执行,同一时刻获取最终结果的问题
Promise.all([read(A), read(A), read(A), read(A)]).then(data => {
    console.log(data);
}).catch(err => {
    console.log(err);
});

Generator

Generator 最大的特点就是可以交出函数的执行权

Generator 函数可以看作是异步任务的容器

需要暂停的地方,都用yield 语法来标注

function * gen() {
    let a = yield 111;
    console.log(a);
    let b = yield 222;
    console.log(b);
    let c = yield 333;
    console.log(c);
    let d = yield 444;
    console.log(d);
}
let t = gen();
t.next(1); // 第一次调用next函数时,传递的参数无效,故无打印结果
t.next(2); // a输出2;
t.next(3); // b输出3;
t.next(4); // c输出4;
t.next(5); // d输出5;

async/await

async 是 Generator 函数的语法糖

async/await 的优点是代码清晰,可以处理回调地狱的问题

function testWait() {
    return new Promise((resolve, reject) => {
        setTimeout(function)(){
            console.log('testWait');
            resolve();
        }, 1000);
    })
}
async function testAwaitUse() {
    await testWait()
    console.log('hello');
    // 输出顺序: testWait,hello
    // 第十行如果不使用 await 输出顺序: hello,testWait
}
console.log(testAwaitUse());

总结

JS 异步编程方式 简单总结
回调函数 早些年 JS 异步编程采用的方式
Promise ES6 新增加异步编程方式,解决回调地狱问题
Generator 和yield配合使用,返回的是迭代器
async/await 二者配合使用,async 返回的是 Promise 对象,await 控制执行顺序