解决异步的方式

js是单线程的,一次只能完成一个任务,如果有多个任务,就需要排队,如果有一个任务耗时很长,那么后边任务就需要等待。为了解决这个问题,js将任务的执行分成两种模式:同步和异步

同步:会阻塞后面程序的运行

“同步模式”就是传统做法,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的。这往往用于一些简单的、快速的、不涉及 IO 读写的操作。

console.log(100)
alert(200)   //只有点击了确认按钮,后面的程序才会接着执行
console.log(300);
异步:不会阻塞后面程序的运行

“异步模式”将每一个任务分成两段,第一段代码包含对外部数据的请求,第二段代码被写成一个回调函数,包含了对外部数据的处理。第一段代码执行完,不是立刻执行第二段代码,而是将程序的执行权交给第二个任务。等到外部数据返回了,再由系统通知执行第二段代码。所以,程序的执行顺序与任务的排列顺序是不一致的、异步的

console.log(100)
setTimeout(function(){
    console.log(200)
},1000)
console.log(300)        //输出100   300   200

什么情况下需要异步:需要等待的情况下

使用异步的场景:

定时任务:setTimeout , setInterval
网络请求:ajax请求,动态img加载
事件绑定

js是怎么实现异步的:事件循环(event-loop)

js将所有任务分为两类:同步任务和异步任务。

同步任务添加到主线程中,所有的同步任务在主线程上排队执行,形成一个执行栈,只有前一个任务执行完毕,才能执行后一个任务。异步任务指的是,不进入主线程,会将异步任务添加到事件线程中,当满足事件触发条件后,将任务添加到事件队列(event-queue)中。

事件队列中的任务何时被执行?

  1. 执行栈中的同步任务执行完毕,即主线程空闲时,此时系统会去查看任务队列,如果有可运行的异步任务就将任务添加到执行栈中开始执行。
  2. 如果任务队列中有多个异步任务待执行,那么这些异步任务也要排队等待被执行,并不是添加到任务队列中的异步任务就会立即执行。

只要主线程空了,就会去读取任务队列,这个过程是循环不断的,这就是javaScript的运行机制,也称作事件循环。

总结:

事件循环:同步的代码直接执行,异步的函数先放在异步队列中,待同步函数执行完毕后,再轮询执行异步队列的函数
在这里插入图片描述
一般说:100ms后会执行setTimeout里的回调函数,这样的说法并不严谨,

准确的说是:第一个setTimeout在100ms后才会被放到异步队列中,第二个setTimeout会立刻放入异步队列,而添加到异步队列的任务,只有等到主线程执行栈中的同步任务全部执行完毕之后,才会被执行,如果主线程执行任务很多,执行时间超过100ms,那么这个函数只能等待。

输出结果3 2 1,先进的先出。

解决异步的五种方法:
1.回调函数(callback)

异步编程最基本的方法:

$.ajax(url,()=>{
	//do something...
	$.ajax(url1,()=>{
		//do something...
		$.ajax(url2,()=>{
			//do something...
		})
	})
})

存在的问题:回调地狱,多个回调嵌套,各个部分之间高度耦合,使得程序结构混乱,流程难以追踪,不能使用try…catch…捕获错误,不能return。

2.事件监听

另一种思路是采用事件驱动模式,任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

什么是事件驱动模式?
js是采用事件驱动(event-driven)响应用户操作的。
比如:通过鼠标或者按键在浏览器窗口或者网页元素(按钮,文本框…)上执行的操作,我们称之为事件(event);
由鼠标或热键引发的一连串程序的动作,称之为事件驱动(event-driver);
对事件进行处理的程序或函数,我们称之为事件处理程序(event handler);

3.Promise

Promise对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。
Promise的思想是:每一个异步任务立刻返回一个promise对象,由于是立刻返回,所以可以采用同步的操作流程,这个promise对象有一个then方法,允许指定回调函数,在异步任务完成后调用。

Promise的三种状态:
Pending——Promise实例创建时的初始状态。
Fulfilled——成功状态
Rejected——失败状态

Pending状态可以转换为Fulfilled状态或者Rejected状态,但是Fulfilled状态或Rejected状态不能转换为Pending状态,也就是说,这种状态转换是不可逆的。

ES6提供了原生的Promise构造函数,用来生成Promise实例:


var promise = new Promise(function(resolve, reject) {
 
  // 异步操作的代码
 
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
 
});

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从Pending变为Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
在这里插入图片描述

4.async函数
5.generator函数

多任务的解决方案,协程(coroutine),意思是多个线程互相协作,完成异步任务。

generator函数是协程在ES6中的实现,最大特点是可以交出函数的执行权。整个generator函数就是一个异步任务的容器,异步操作需要暂停的地方,都用yield语句注明。

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

yield表达式只能用在 Generator 函数里面,用在其他地方都会报错。


function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
var hw = helloWorldGenerator();

以上代码定义了一个generator函数helloWorldGenerator,内部有两个yield表达式(“hello” “world”),和一个return语句(结束执行)。

调用helloWorldGenerator函数后,并不执行,而是返回一个遍历器对象(lterator),它是一个指向内部状态的指针。

然后可以调用遍历器对象的next()方法,每次调用该方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield语句或return语句为止。

换言之,generator函数是分段执行的,yield表达式是暂停执行的标记,next方法可以恢复执行。

hw.next()
 
// { value: 'hello', done: false }
 
hw.next()
 
// { value: 'world', done: false }
 
hw.next()
 
// { value: 'ending', done: true
 
hw.next()
 
// { value: undefined, done: true }

第一次调用,Generator 函数开始执行,直到遇到第一个yield表达式为止。next方法返回一个对象,它的value属性就是当前yield表达式的值hello,done属性的值false,表示遍历还没有结束。

第二次调用,Generator 函数从上次yield表达式停下的地方,一直执行到下一个yield表达式。next方法返回的对象的value属性就是当前yield表达式的值world,done属性的值false,表示遍历还没有结束。

第三次调用,Generator 函数从上次yield表达式停下的地方,一直执行到return语句(如果没有return语句,就执行到函数结束)。next方法返回的对象的value属性,就是紧跟在return语句后面的表达式的值(如果没有return语句,则value属性的值为undefined),done属性的值true,表示遍历已经结束。

第四次调用,此时 Generator 函数已经运行完毕,next方法返回对象的value属性为undefined,done属性为true。以后再调用next方法,返回的都是这个值。

总结一下,调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。

在这里插入图片描述

generator函数和async函数的区别

1、generator函数被调用后返回一个遍历器对象(lterator),async函数返回一个promise对象
2、async函数内置执行器,可以像普通函数那样调用,generator函数需要使用co模块来实现流程控制或者自定义流程控制。
3、async是generator的语法糖

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值