js中的同步与异步的理解

一,正常情况下同步代码

通常情况下,我们的代码执行顺序是从上到下,按照顺序执行。

console.log('1');
const a = test();
console.log(a);
function test() {
  console.log('2');
  return '函数返回';
}
console.log('3');

这个代码片段的返回结果是:

12,函数返回,3

二,有异步的代码执行情况

console.log('1');
const a = test();
console.log(a);
function test() {
  setTimeout(() => {
    //setTimeout是异步操作
    console.log('2');
    return '函数返回';
  }, 1000);
}
console.log('3');

这个代码片段的执行结果便是:

1,undefined,3,2

三,总结原因-同步阻塞与异步非阻塞

因为js是单线程机制的,所有的操作都必须一个一个来,如果中间有一个操作非常耗时,那整个线程都会阻塞在那里,这就是同步阻塞
为了解决这个问题,js引入了事件和回调函数机制,对于一个IO操作,比如一个ajax,当发出一个异步请求后,程序不会阻塞在那里等待结果的返回,而是继续执行下面的代码。当请求成功获取到结果后,就会调用回调函数来处理后面的事情,这个就是异步非阻塞
具体这个概念可以看我之前的文章:https://blog.csdn.net/weixin_42349568/article/details/110351387

对应于一中正常的代码,就是同步阻塞,等一行代码执行完毕后才能执行下一行代码。
对应于二中的异步的代码,就是异步非阻塞,发现是异步后,不会阻塞住,而是继续执行后续的代码,等异步结果出来后,再执行。

四,回调函数的产生和应用

有时候,我们需要在异步操作后再进行某些操作。于是可以在异步操作后再调用一个函数,利用的就是异步操作结果出来后,其内部代码顺序执行的原理:

console.log('1');
test(callback);
function test(cb) {
  setTimeout(() => {
    //setTimeout是异步操作
    const a = 2;
    cb(a);
  }, 1000);
}
function callback(val) {
  console.log('回调函数执行', val);
}
console.log('3');
执行结果:13,回调函数执行2

如果第一个异步API执行的结果,需要给第二个异步api作为参数使用。但是第二个异步api在第一个异步api还没有执行完毕返回结果就执行了,那怎么办?

function asyncFn1(cb) {
  setTimeout(() => {
    console.log('这是第一个异步操作');
    const a = 1;
    cb(a);
  }, 1000);
}
function asyncFn2(val) {
  setTimeout(() => {
    console.log('这是第二个异步操作', val);
  }, 2000);
}
asyncFn1(asyncFn2);

也就是把函数作为参数,然后取得结果再执行。但是回调的层级多了,就会很麻烦,写着不爽,调试更是绝望。也就是人们说的回调地狱。
于是业界产生了一种比较好的解决方案,promise。

五,promise的使用

引入promise的最大作用,就是把异步操作的过程和结果做到了分离,可以用promise.then()来获取和处理异步操作的结果。

function asyncFn1() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('这是第一个异步操作', 222);
      const a = 1;
      resolve(a);
    }, 1000);
  });
}
function asyncFn2(val) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('这是第二个异步操作', val);
      resolve();
    }, 2000);
  });
}
console.log('1111');
asyncFn1().then(res => {
  asyncFn2(res);
});
console.log('3333');

打印的结果将会是:

1111
3333
这是第一个异步操作 222
这是第二个异步操作 1

可以参考这张图理解:在这里插入图片描述

主线程按顺序执行代码

1,执行console.log('1111');
2,执行asyncFn1,遇到setTimeout发现是异步,注册一下
3,执行console.log('3333')(因为js是异步非阻塞,所以这里继续执行后面的代码)
4,第一个异步setTimeout执行完毕了, console.log('这是第一个异步操作', 222);
5,因为使用了promise,所以会等第一个异步执行完毕,才开始执行then中的第二个异步

主要就是理解这句话,js是异步非阻塞的,同步执行,异步注册后等待执行完毕。promise可以将回调函数的处理转化成链式调用,做到异步操作和结果的分离。
需要注意的是,**promise并不会产生阻塞效果,它只对它包裹的异步操作负责,只能够实现它包裹的异步操作执行完毕后,才会执行.then中的代码。**所以这里console.log(‘3333’)会先执行。
换句话说,promise只是局部的,受用户控制的顺序执行,采用的方案是promise包裹异步操作,resolve()返回异步结果,然后.then中处理异步结果。

六,async和await

async和await就更进一步,它能够将promise中的异步转化为同步,也就是说,它能产生阻塞效果,等待它后面的代码执行完毕后才会继续往下执行。
值得注意的是,await后面必须是一个promise对象才可以。

function asyncFn1() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('这是第一个异步操作', 222);
      const a = 1;
      resolve(a);
    }, 1000);
  });
}
function asyncFn2(val) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('这是第二个异步操作', val);
      resolve();
    }, 2000);
  });
}
console.log('1111');
const result = await asyncFn1();
await asyncFn2(result);
console.log('3333');

执行的结果:

1111
这是第一个异步操作 222
这是第二个异步操作 1
3333
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值