【JavaScript】异步JavaScript

什么是异步

在JavaScript中,程序的执行是顺序的,是单线程的,即一次只能做一件事情

比如:页面在加载图片或进行大量运算时,用户将无法进行其它操作

这个时候控制权并没有在用户手上,整个浏览器就像冻结一样,这叫做阻塞

异步就是为了能够解决这类问题而出现的

异步实现

1. 异步callbacks(旧)

1.1 基本用法

异步callbacks就是函数,且该函数作为参数传递给其它在后台执行的函数;

当后台运行结束,就通过调用callbacks函数,通知你工作已经完成(捕获到事件)

如:addEventListener()第二个参数

当监听到click事件的时候,就会调用回调函数(callback)

btn.addEventListener('click', () => {
  alert('You clicked me!');

  let pElem = document.createElement('p');
  pElem.textContent = 'This is a newly-added paragraph.';
  document.body.appendChild(pElem);
});

 1.2 回调地狱

下面这个例子来自MDN官方文档

“ 我们来谈谈订购披萨作为类比。为了使你的订单成功,你必须按顺序执行,不按顺序执行或上一步没完成就执行下一步是不会成功的:

  1. 选择配料。如果你是优柔寡断,这可能需要一段时间,如果你无法下定决心或者决定换咖喱,可能会失败。
  2. 下订单。返回比萨饼可能需要一段时间,如果餐厅没有烹饪所需的配料,可能会失败。
  3. 然后你收集你的披萨吃。如果你忘记了自己的钱包,那么这可能会失败,所以无法支付比萨饼的费用!“
chooseToppings(function(toppings) {
  placeOrder(toppings, function(order) {
    collectOrder(order, function(pizza) {
      eatPizza(pizza);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

对于每个回调函数都需要有一个failureCallback(),使得代码十分冗余

2. Promises(新)

2.1 基本用法

使用fetch来获取事件

then用来包含要执行的回调操作,且每个回调都接收前一个成功操作的结果作为输入,可对传过来的结果继续进行操作

catch用于捕获所有then中的错误

fetch('products.json').then(function(response) {
  return response.json();
}).then(function(json) {
  products = json;
  initialize();
}).catch(function(err) {
  console.log('Fetch problem: ' + err.message);
});

2.2 Promise术语

1.创建promise时,其所处状态为pending(待定)

2.当promise返回时,称为resolved(已解决)

  2.1 一个成功resolved的promise称为fullfilled(实现)。它返回一个值,可以通过.then()块链接到promise链的末尾来访问该值。.then()块中的执行程序函数将包含promise的返回值。

  2.2一个未能resolved的promise称为rejected(拒绝)。它返回一个原因(reason),一条说明为什么拒绝的错误消息。可以通过将.catch()块链接到promise链的末尾来访问此原因。

2.3 Promise fullfill/reject后运行一些最终代码

将.finally()方法链接到promise链的末尾即可

myPromise
.then(response => {
  doSomething(response);
})
.catch(e => {
  returnError(e);
})
.finally(() => {
  runFinalCode();
});

  

3. async和await

3.1 基本用法

将async关键字放在函数声明前,使其称为async function(异步函数)

如下,hello将返回一个promise对象

async function hello() { return "Hello" };
hello();

由上一小节2.Promises可知,可以使用.then()块进行后续操作

hello().then((value) => console.log(value))

await关键字只在异步函数里面才起作用

重写2.Promises中代码

async function myFetch(){
    let response = await fetch('products.json');
    let products = await response.json;
    initialize();
}

myFetch()
.catch(err=>{
    console.log('Fetch problem: ' + err.message);
})

可以看到通过关键字可以除去.then()代码块,代码更加简单易懂

3.2 代码时如何工作的?

Javascript运行到await时将暂停,并允许其它代码在此期间执行,直到异步函数调用返回其结果

let response = await fetch('products.json');

解析器会在此行上暂停,直到当服务器返回的响应变得可用时。

此时fetch()返回的promise将会完成(fullfilled)并赋值给response变量

接着解析器便会移动到下一行代码

 此时products才能获取到服务器响应返回的数据

    let products = await response.json;

  

4. 超时和间隔

4.1 setTimeout()

在指定的时间后执行一段代码.

setTimeout前两个参数必填,最后一个参数选填

第一个参数为要超时执行的函数

第二参数为超时事件

第三个为传入函数中参数(可选)

则这段代码效果为: 在执行sayHi函数前,系统将停留2000毫秒,并且传递‘Mr. GoodLooking’给sayHi()函数

function sayHi(who) {
  alert('Hello Mr. Universe!');
}

let myGreeting = setTimeout(sayHi, 2000, 'Mr. GoodLooking');

清除(取消)超时

clearTimeout(myGreeting);

  

4.2 setInterval()

以固定的时间间隔,重复运行一段代码.

第二个参数表示每隔1000毫秒执行一次函数displayTime()

function displayTime() {
   let date = new Date();
   let time = date.toLocaleTimeString();
   document.getElementById('demo').textContent = time;
}

const createClock = setInterval(displayTime, 1000);

  

 清除(取消)间隔

const myInterval = setInterval(myFunction, 2000);

clearInterval(myInterval);

  

4.3 requestAnimationFrame()

setInterval()的现代版本;在浏览器下一次重新绘制显示之前执行指定的代码块,从而允许动画在适当的帧率下运行,而不管它在什么环境中运行.

function draw() {
   // Drawing code goes here
   requestAnimationFrame(draw);
}

draw();

  

参考资料:

https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Asynchronous

原文地址:https://www.cnblogs.com/leftstan/p/14800097.html