javascript是一门单线程的语言,也就是说一次只能完成一件任务,如果有多个任务,就需要排队进行处理。如果一个任务耗时很长,后面的任务也必须排队等待,这样大大的影响了整个程序的执行。为了解决这个问题,javascript语言将任务分为两种模式:
同步:当我们打开网站,网页的页面骨架渲染和页面元素渲染,就是一大推同步任务。
异步:我们在浏览新闻时,加载图片或音乐之类占用资源大且耗时久的任务就是异步任务。
本文主要针对近两年javascript的发展,主要介绍异步处理的进化史。目前,在javascript异步处理中,有以下几种方式:
callback
回调函数是最早解决异步编程的方法。无论是常见的setTimeout还是ajax请求,都是采用回调的形式把事情在某一固定的时刻进行执行。
 //常见的:setTimeout  setTimeout(function callback(){   console.log('aa'); }, 1000); //ajax请求 ajax(url,function callback(){ console.log("ajax success",res); })
回调函数的处理一般将函数callback作为参数传进函数,在合适的时候被调用执行。回调函数的优点就是简单、容易理解和实现,但有个致命的缺点,容易出现回调地狱(Callback hell),即多个回调函数嵌套使用。造成代码可读性差、可维护性差且只能在回调中处理异常。
ajax(url, () => { //todo ajax(url1, () => { //todo ajax(url2, () => { //todo }) }) })
事件监听
事件监听采用的是事件驱动的模式。事件的执行不取决于代码的顺序,而是某个事件的发生。
假设有两个函数,为f1绑定一个事件(jQuery的写法),当f1函数发生success事件时,执行函数f2:
f1.on('success',f2);
对f1进行改写:
function f1(){ ajax(url,() => { //todo f1.trigger('success');//触发success事件,从而执行f2函数 }) }
事件监听的方式较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合",有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。阅读代码的时候,很难看出主流程。
发布订阅
我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做 发布/订阅模式(publish-subscribe pattern),又称**观察者模式"(observer pattern) **。
//利用jquery的插件实现 //首先,f2向消息中心订阅success事件 jQuery.subscribe('success',f2); //对f1进行改写: function f1(){ ajax(url,() => { //todo jQuery.publish('success');//当f1执行完毕后,向消息中心jQuery发布success事件,从而执行f2函数 }) } //f2执行完毕后,可以取消订阅 jQuery.unsubscribe('success',f2)
该方法和事件监听的性质类似,但我们可以通过消息中心来查阅一共有多少个信号,每个信号有多少个订阅者。
Promise
****是CommonJS工作组提出的一种规范,可以获取异步操作的消息,也是异步处理中常用的一种解决方案。Promise的出现主要是用来解决回调地狱、支持多个并发的请求,获取并发请求的数据并且解决异步的问题。
let p = new Promise((resolve, reject) => { //做一些异步操作 setTimeout(()=>{ let num = parseInt(Math.random()*100); if(num > 50){ resolve("num > 50"); // 如果数字大于50就调用成功的函数,并且将状态变成Resolved }else{ reject("num <50");// 否则就调用失败的函数,将状态变成Rejected } },10000) }); p.then((res) => { console.log(res); }).catch((err) =>{ console.log(err); })
Promise有三种状态:等待pending、成功fulfied、失败rejected;状态一旦改变,就不会再变化,在Promise对象创建后,会马上执行。等待状态可以变为fulfied状态并传递一个值给相应的状态处理方法,也可能变为失败状态rejected并传递失败信息。任一一种情况出现时,Promise对象的 then 方法就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,均为 Function。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法)。
需要注意的是: Promise.prototype.then 和 Promise.prototype.catch 方法返回promise 对象, 所以可以被链式调用,如下图:
Promise的方法: