五 异步

106 阅读2分钟

题目

同步和异步的区别是什么?

手写Promise加载一张图片

前端使用异步的场景有哪些?

下方打印顺序

console.log(1)
setTimeout(function(){
  console.log(2)
}, 1000);
console.log(3)
setTimeout(function(){ // 不管是几秒后执行,它也是个异步,先执行同步代码后再去执行,然后等待1秒后再执行另一个setTimeout
  console.log(4)
}, 0);
console.log(5)

// 1 3 5 **4 2**
先执行4时间短,再执行2 因为要等1

知识点

单线程和异步

应用场景

callback hell和Promise

  • callback本身没有问题,callback多了就嵌套成hell了,就有问题了;
  • 不是解决callback本身,是解决callbakc嵌套

单线程和异步

单线程是异步的背景

JS是单线程语言,只能同时做一件事儿

浏览器和nodejs已支持JS启动进程,如Web Worker

JS和DOM渲染共用同一个进程,互相阻塞,因为JS可修改DOM结构

遇到等待(网络请求,定时任务)不能卡住

需要异步(解决单线程等待的问题)

回调callback函数形式

面试题一:同步和异步的区别是什么

基于JS单线程语言

异步不会阻塞代码执行

同步会阻塞代码执行

// 异步【通过callback回调函数来执行的】,不会阻塞后面的执行
console.log(100)
setTimeout(()=>{
  console.log(200)
}, 1000)
console.log(300)
// 同步 alet(200)会卡住后面的代码
console.log(100)
alert(200)
console.log(300)

面试题二:应用场景(等待的场景)

等待的过程中,cpu不能闲着,所以需要异步,让后面的代码继续执行

  • 网络请求,如ajax图片加载
  • 定时任务,如setTimeout
// ajax
console.log('start');
$.get('./data1.json', function(data1){
  console.log(data1);
})
console.log('end')
// 打印顺序
// start
// end
// data

解释
// 先打印 start
// 再执行网络请求,让它先请求,继续往下执行
// 再打印 end
// 等网络请求完,执行回调函数,最后打印data
console.log('start');
let img = document.createElement('img');
img.onload = function(){
  console.log('loaded')
}
img.src = './xxx.png';
console.log('end')
// 打印顺序
// start
// end
// loaded

解释
// 先打印start
// 定义img
// 定义img.onload,就是callback的一种形式, 赋值成callback函数的形式
// src赋值,src一旦赋值之后,img.onload就会触发加载, 
// 再打印end:img.src加载过程中,就不管了,就先打印 end,
// 等 img.src加载完成后,img.onload就会触发加载
// setTimeout
console.log(100)
setTimeout(function(){
  console.log(200)
}, 1000)
console.log(300)

// 100
// 300
// 200
// setInterval
console.log(100)
setInterval(function(){
  console.log(200)
}, 1000)
console.log(300)

// 100
// 300
// 200
// 200
// 200
...

call hell

异步是基于回调函数的形式执行的,越陷越深,但这个方式可读性很差,回调满天飞

$.get(url1, (data)=>{
  console.log(data1)

  // 获得第二份数据
  $.get(url2, (data2)=>{
    console.log(data2)

    // 获得第三份数据
    $.get(url3, (data3)=>{
      console.log(data3)

      // 还可能获取更多的数据
    })
  })
})

手写Promise 网络请求

  • promise 将callback变成了管道串联形式,是非嵌套形式,而不是callback hell的嵌套形式,提高代码可读性
function getData(){
  return new Promise((resolve, reject) => {
    $.ajax({
      url,
      success(data){
        resolve(data)
      },
      error(err){
        reject(err)
      }
    })
  })
}

const url1 = '/data1.json';
const url2 = '/data2.json';
const url3 = '/data3.json';

getData(url1)
  .then((data1)=>{
    console.log(data1)
    return getData(url2)
  })
  .then((data2=>{
    console.log(data2)
    return getData(url3)
  }))
  .then((data3=>{
    console.log(data3)
  }))
  .catch(err => console.log(err)) // 处理上方报错

面试题三:手写Promise加载图片

function loadImg(src){
  return new Promise((resolve, reject) => {
    let img = document.createElement('img');
    // 图片加载完成后执行的回调函数img.onload,promise不是避免了回调函数,而是把回调函数这种形式改为串联的形式
    img.onload = function(){
      resolve(img)
    }
    img.onerror = function(){
      const err = 
      reject(new Error(`图片加载失败 ${src}`))
    }

    // 这句话一赋值完成,
    // 成功就触发上方的onload函数,调用 resolve把 img传出去
    // 失败就触发onerror函数,调用 reject把 err抛出去
    img.src = src;
  })
}
// 1 用resolve
const src = '//www.baidu.com/img/flexible/logo/pc/result@2.png';
loadImg(src)
  .then(img => {
    console.log(img.width)
    return img
  })
  .then(img => {
    console.log(img.height)
  })
  .catch(err => console.log(err))
  // 404
  // 131
// 2 用reject
const src = '//1www.baidu.com/img/flexible/logo/pc/result@2.png';
loadImg(src)
.then(img => {
  console.log(img.width)
  return img
})
.then(img => {
  console.log(img.height)
})
.catch(err => console.log(err))
// Error: 图片加载失败 //1www.baidu.com/img/flexible/logo/pc/result@2.png
// 3 两张图片
const src1 = '//www.baidu.com/img/flexible/logo/pc/result@2.png';
const src2 = 'https://img1.mukewang.com/5477304c00014a4c01320132-100-100.jpg'
loadImg(src1)
  .then(img1 => {
    console.log(img1.width)
    return img1 // 普通对象,其后的then就接收这个普通对象
  })
  .then(img1 => {
    console.log(img1.height)
    // promise实例, 用promise来解决地狱回调的问题,其后的then接收这个实例的resolve中的参数
    return loadImg(src2) 
  })
  .then(img2 => {
    console.log(img2.width)
    return img2
  })
  .then(img2 =>{
    console.log(img2.height)
  })
  .catch(err => console.log(err))

function getData(){
  return new Promise((resolve, reject) => {
    $.ajax({
      url,
      success(data){
        resolve(data)
      },
      error(err){
        reject(err)
      }
    })
  })
}
// 404 // 第一张图的宽
// 132 // 第一张图的高
// 100 // 第二张图的宽
// 100 // 第二张图的高