LLwanran / front_end_studying

前端知识要点
https://llwanran.github.io/front_end_studying/
2 stars 1 forks source link

滴滴: JS异步解决方案的发展历程以及优缺点(一面) #58

Open LLwanran opened 4 years ago

LLwanran commented 4 years ago

答案:

什么是异步:

当前一个任务被执行时,不会等待任务执行完成后就去执行下一个任务,等前一个任务执行完成后,将去执行其返回的回调函数,这是异步操作

为什么要用异步:

js是单线程的,因此必须等前一个任务完成后,后一个任务才会被执行。因此当执行一段耗时的程序时,会影响整个程序的执行,异步的方法就是为了解决这个问题。

解决方案:

1.回调函数callback:

被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数。如setTimeOut,ajax请求,readFile等。
例:

function greeting(name) {
  alert('Hello ' + name);
}

function processUserInput(callback) {
  var name = prompt('请输入你的名字。');
  callback(name);
}

processUserInput(greeting);

优点:
解决了异步的问题。
缺点:
回调地狱:多个回调函数嵌套的情况,使代码看起来很混乱,不易于维护。

2.事件发布订阅

当一个任务执行完成后,会发布一个事件,当这个事件有一个或多个‘订阅者’的时候,会接收到这个事件的发布,执行相应的任务,这种模式叫发布订阅模式。如node的events,dom的事件绑定
例:

document.body.addEventListener('click',function(){
  alert('订阅了');
},false);
document.body.click(); 

优点:时间对象上的解耦。
缺点:消耗内存,过度使用会使代码难以维护和理解

3.Promise

Promise是es6提出的异步编程的一种解决方案。
Promise 对象有三种状态:

let promise = new Promise(function (resolve, reject) {
    fs.readFile('./1.txt', 'utf8', function (err, data) {
        resolve(data)
    })
})

promise
    .then(function (data) {
        console.log(data)
    })

优点:解决了回调地狱的问题,将异步操作以同步操作的流程表达出来。
缺点:无法取消promise。如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。当执行多个Promise时,一堆then看起来也很不友好。

4.Generator

Generator是es6提出的另一种异步编程解决方案,需要在函数名之前加一个*号,函数内部使用yield语句。Generaotr函数会返回一个遍历器,可以进行遍历操作执行每个中断点yield。
例:

function * count() {
  yield 1
  yield 2
  return 3
}
var c = count()
console.log(c.next()) // { value: 1, done: false }
console.log(c.next()) // { value: 2, done: false }
console.log(c.next()) // { value: 3, done: true }
console.log(c.next()) // { value: undefined, done: true }

优点:没有了Promise的一堆then(),异步操作更像同步操作,代码更加清晰。
缺点:不能自动执行异步操作,需要写多个next()方法,需要配合使用Thunk函数和Co模块才能做到自动执行。

5.async/await

async是es2017引入的异步操作解决方案,可以理解为Generator的语法糖,async等同于Generator和co模块的封装,async 函数返回一个 Promise。
例:

async function read() {
 let readA = await readFile('data/a.txt')
 let readB = await readFile('data/b.txt')
 let readC = await readFile('data/c.txt')

 console.log(readA)
 console.log(readB)
 console.log(readC)
}

read()

优点:内置执行器,比Generator操作更简单。async/await比*/yield语义更清晰。返回值是Promise对象,可以用then指定下一步操作。代码更整洁。可以捕获同步和异步的错误。