1.4代码模拟 JavaScript 异步编程

同步模式与异步模式

同步模式下代码的执行顺序

console.log('global begin'); // 1. 将此行压入调用栈执行,输出到控制台

function bar() {
  console.log('bar task'); // 5. 将此行压入调用栈执行,输出到控制台
}// 6. 将 bar() 函数从调用栈弹出

function foo() {
  console.log('foo task'); // 3. 将此行压入调用栈执行,输出到控制台
  bar(); // 4. 将 bar 压入调用栈执行 bar() 中的代码
}// 7. 将 foo() 函数从调用栈弹出

foo(); // 2. 将 foo 压入调用栈执行 foo() 中的代码

console.log('global end'); // 8. 将此行压入调用栈执行,输出到控制台

异步模式

执行过程

  • 为了解决同时处理大量耗时任务的问题
  • 图片解析 async 方法中代码执行过程
console.log('global begin');

setTimeout(function timmer1() {
  console.log('timer1 invoke');
}, 1800);

setTimeout(function timer2() {
  console.log('timer2 invoke');

  setTimeout(function inner() {
    console.log('inner invoke');
  }, 1000);
}, 1000);

console.log('global end');
  1. 将整块代码压入调用栈异步模式执行顺序1
  2. console.log(‘global begin’); 压入调用栈输出控制台并立即弹出异步模式执行顺序2
  3. 调用栈将 timer1 压入,通知 WebAPI 独立于当前线程开启计时器,并立即弹出调用栈异步模式执行顺序3
  4. 调用栈将 timmer2 压入,WebAPI 开启计时器 timmer2,调用栈立即弹出 timmer2异步模式执行顺序4
  5. 调用栈将 console.log(‘global end’); 压入,输出控制台并立即弹出,接着将整个 async 代码片段弹出异步模式执行顺序5
  6. 此时调用栈被清空,事件循环侦听消息队列中并无待办项异步模式执行顺序6
  7. 倒计时执行条件满足后依次执行:timmer 2 中的代码被推入消息队列,事件循环侦听到消息队列中有代办项,并且调用栈为空,将 timmer 2 压入调用栈;将 console.log(‘timmer2 invoke’) 压入调用栈输出到控制台并且立即弹出异步模式执行顺序7
  8. 调用栈将 setTimOut(inner) 推入栈中,通知 WebAPI 开启计时器 inner,并依次将 setTimout(inner)timmer2() 弹出,整个过程完成后消息队列和调用栈均为空异步模式执行顺序8
  9. timmer1 倒计时结束率先触发,消息队列将 timmer1 中的语句入列,此时事件循环立即通知调用栈将 timmer1 取走执行,完成之后消息队列和调用栈依次被清空异步模式执行顺序9
    10.inner 倒计时结束,消息队列将 inner 中的语句入列,此时事件循环立即通知调用栈取走执行,输出到控制台语句分别入栈、执行、出栈后整个过程结束异步模式执行顺序10

开启一个异步线程执行流程图

异步线程解析

异步编程的几种方式

Promise 概述

回调函数

回调函数可以理解为一件你想要做的事情

CommomJS 提出 Promise 规范

Promise概念

Promise 常见误区

  • 不采用链式调用也会造成回调地狱

Promise 链式调用

  • then 方法的回调函数返回的 promise 对象是另外一个新的实例
  • 后面的 then 方法就是在为上一个 then 返回的 Promise 注册回调
  • 前面 then 方法中回调函数的返回值会作为后面 then 方法回调的参数
  • 如果回调中返回的是 promise,那么后面的方法会等待返回再继续执行

Promise 异常处理

  • 推荐在链式调用的末尾统一处理

Promise 静态方法

  • Promise.resove
  • Promise.reject

Promise 并行执行

  • Promise.all
  • Promise.race

Promise 执行时序

  • es6 当中微任务概念的引入
  • Promise 的回调函数会作为微任务率先执行
function log(...args) {
  console.log(args.join(', '));
}

log('global start');

setTimeout(() => {
  log('timeout'); // 等待 promise 微任务执行完成之后最后执行
}, 0);

// 微任务:在当前任务结束过后立即执行的任务
Promise.resolve()
  .then(() => {
    log('p1');
  })
  .then(() => {
    log('p2');
  })
  .then(() => {
    log('p3');
  });

log('global end');

Generator 的使用

generator 异步调用

function log(...args) {
  console.log(args.join(', '));
}

function* foo() {
  log('generator foo');

  try {
    const res = yield 'foo';
    log(res);
  } catch (e) {
    console.log(e);
  }
}

const generator = foo();

const res1 = generator.next();
log(JSON.stringify(res1));

// 1. 传参入 foo
// const res2 = generator.next('scope');
// log(JSON.stringify(res2));

// 2. 抛出异常给 foo
// const res2 = generator.throw(new Error('generator error'));
// log(JSON.stringify(res2));

// 3. 返回值
const res2 = generator.return('return');
log(JSON.stringify(res2));

generator 典型用例

function ajax(url) {
  return new Promise(function (resolve, reject) {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.responseType = 'json';
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    xhr.send();
  });
}

function* model() {
  const { data: user } = yield ajax('http://localhost:3001/api/userData');
  console.log(user);

  const { data: post } = yield ajax('http://localhost:3001/api/postData');
  console.log(post);
}

const generator = model();
const result = generator.next();
const { value: userAction } = result;

userAction.then(user => {
  const { value: postAction } = generator.next(user);

  postAction.then(post => {
    generator.next(post);
  });
});

带来的问题,重复调用有复杂的代码

generator 用例优化

function ajax(url) {
  return new Promise(function (resolve, reject) {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.responseType = 'json';
    xhr.onload = function () {
      switch (this.status) {
        case 200:
          resolve(this.response);
          break;
        case 404:
          reject(new Error('找不到的异常'));
          break;
        default:
          reject(new Error(this.statusText));
          break;
      }
    };
    xhr.send();
  });
}

function* model() {
  const { data: user } = yield ajax('http://localhost:3001/api/userData');
  console.log(user);

  const { data: post } = yield ajax('http://localhost:3001/api/postData');
  console.log(post);

  const err = yield ajax('/api/errData');
  console.log(err);
}

function co(generator, params) {
  const step = generator.next(params);
  const { value, done } = step;
  if (done) return;
  value.then(
    data => {
      co(generator, data);
    },
    e => {
      generator.throw(e);
    },
  );
}

Async Await 方案

function ajax(url) {
  return new Promise(function (resolve, reject) {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.responseType = 'json';
    xhr.onload = function () {
      switch (this.status) {
        case 200:
          resolve(this.response);
          break;
        case 404:
          reject(new Error('找不到的异常'));
          break;
        default:
          reject(new Error(this.statusText));
          break;
      }
    };
    xhr.send();
  });
}

async function main() {
  const { data: user } = await ajax('http://localhost:3001/api/userData');
  console.log(user);

  const { data: post } = await ajax('http://localhost:3001/api/postData');
  console.log(post);

  const err = await ajax('/api/errData');
  console.log(err);
}

main(); // 调用时省略了 co 函数

项目源代码地址

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值