100-js-16-异步

单线程和异步

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

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

JS 和 DOM 渲染共用同一个线程,因为 JS 可修改 DOM 结构

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

需要异步

回调 callback 函数形式

异步和同步的区别

异步不会阻塞代码执行

// 异步 通过(callback 回调函数)执行
console.log(100)
setTimeout(() => {
    console.log(200)
}, 1000)
console.log(300)

同步会阻塞代码执行

// 同步
console.log(100)
alert(200)
console.log(300)

异步的应用场景

网络请求

ajax

// ajax
console.log('start')
$.get('./data.json', function(data) {
  console.log(data)
})
console.log('end')

图片加载

// 图片加载
console.log('start')
let img = document.createElement('img')
img.onload = function() {
  console.log('onload')
}
img.src = '/xx.png'
console.log('end')

定时任务

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

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

promise

主要是用来解决回调地狱的问题,所以出现了promise 。现在主流的方法是使用async 和 await 来进行解决异步问题。 async 和 await 本质上是一个语法糖 使函数的返回值包含在promise中返回。

回调地狱

嵌套格式

$.get(url1, (data1) => {
  console.log(data1)
  $.get(url2, (data2) => {
    console.log(data2)
    $.get(url3, (data3) => {
      console.log(data3)
      // ...
    })
  })
})

promise

管道格式

function getData(url) {
  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加载图片

如果在 then 中 return 一个普通对象,下一个then中函数的参数会接收到;
如果在 then 中 return 一个Promise实例的话,下一个then 中函数接收到的参数就是新 Promise 的 resovle 的参数

function loadImg(src) {
    const p = new Promise(
        (resolve, reject) => {
            const img = document.createElement('img')
            img.onload = () => {
                resolve(img)
            }
            img.onerror = () => {
                const err = new Error(`图片加载失败 ${src}`)
                reject(err)
            }
            img.src = src
        }
    )
    return p
}

// const url = 'https://img1.baidu.com/it/u=681100297,1255002990&fm=253&fmt=auto&app=120&f=JPEG?w=890&h=500'
// loadImg(url).then(img => {
//     console.log(img.width)
//     return img
// }).then(img => {
//     console.log(img.height)
// }).catch(ex => console.error(ex))

const url1 = 'https://img1.baidu.com/it/u=681100297,1255002990&fm=253&fmt=auto&app=120&f=JPEG?w=890&h=500'
const url2 = 'https://img2.baidu.com/it/u=2412886502,2778604333&fm=11&fmt=auto&gp=0.jpg'

loadImg(url1).then(img1 => {
    console.log(img1.width)
    return img1 // 普通对象
}).then(img1 => {
    console.log(img1.height)
    return loadImg(url2) // promise 实例
}).then(img2 => {
    console.log(img2.width)
    return img2
}).then(img2 => {
    console.log(img2.height)
}).catch(ex => console.error(ex))

三种状态

1.pending
2.resloved
3.rejected

状态变化:
pending-> resloved 或者 pending->rejected
状态的变化是不可逆的

状态的表现:
1.pending状态:不会触发then或catch
2.resolved状态:会触发后续的then回调函数
3.rejected状态:会触发后续的catch回调函数

then和catch改变状态:

1.then正常返回resolved,里面有报错则返回rejected
2.catch正常返回resolved,里面有报错则返回rejected

// then() 一般正常返回 resolved 状态的 promise
Promise.resolve().then(() => {
    return 100
})

// then() 里抛出错误,会返回 rejected 状态的 promise
Promise.resolve().then(() => {
    throw new Error('err')
})

// catch() 不抛出错误,会返回 resolved 状态的 promise
Promise.reject().catch(() => {
    console.error('catch some error')
})

// catch() 抛出错误,会返回 rejected 状态的 promise
Promise.reject().catch(() => {
    console.error('catch some error')
    throw new Error('err')
})