Skip to content

Latest commit

 

History

History
570 lines (448 loc) · 12.1 KB

File metadata and controls

570 lines (448 loc) · 12.1 KB

异步

目标

  • 什么是单线程,和异步有什么关系
  • 什么是 event-loop
  • 是否用过 jQuery 的 Deferred
  • 目前 js 解决异步的方案有哪些
  • 如果只有 jq 如何解决异步
  • Promise 的基本使用和原理
  • async、await 的使用(ES2017)(和 Promise 的区别、联系)

单线程

什么是单线程,和异步的关系:

  • 单线程 - 只有一个线程,只能做一件事
  • 原因 - 避免 DOM 渲染的冲突
  • 解决方案 - 异步

原因 - 避免 DOM 渲染冲突:

  • 浏览器需要渲染 DOM
  • JS 可以修改 DOM 结构
  • JS 执行的时候,浏览器 DOM 渲染会暂停
  • 两段 JS 也不能同时执行(都修改 DOM 就冲突了)
  • webworker 支持多线程,但是不能访问 DOM

解决方案 - 异步:

  • 问题一:没按照书写方式执行,可读性差
  • 问题二:callback 中不容易模块化

总结:

  • 单线程就是同时只做一件事,两段 JS 不能同时执行
  • 原因就是为了避免 DOM 渲染的冲突
  • 异步是一种"无奈"的解决方案,虽然有很多问题
console.log('start')
var i, sum = 0;
for (i = 0; i < 1000000000; i++) {
    sum++
}
console.log(sum)


console.log(100)
alert('hello world')
console.log(200)


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


console.log(100)
$.ajax({
    url: './data.json',
    success: function (result) {
        console.log(result)
    }
})
console.log(300)
console.log(400)

event-loop

  • 单线程 - 同时间只能做一件事
  • 原因 - 避免 DOM 渲染冲突
  • 解决方案 - 异步
  • 实现方式 - event loop

文字解释:

  • 事件轮询,JS 实现异步的具体解决方案
  • 同步代码,直接执行
  • 异步函数先放在'异步队列'中
  • 待同步函数执行完毕,轮询执行'异步队列'的函数

实例分析:

setTimeout(function () {
    // 异步
    console.log(100)
})
console.log(200)


// 主进程
console.log(200)

// 异步队列
function () {
    console.log(100)
}
setTimeout(function () {
    console.log(1)
}, 1000)
setTimeout(function () {
    console.log(2)
})
console.log(3)


// 主进程
console.log(3)

// 异步队列
// 立即被放入
function () {
    console.log(2)
}

// 1000ms之后被放入
function () {
    console.log(1)
}
$.ajax({
    url: './data.json',
    success: function (res) {
        console.log('a')
    }
})
setTimeout(function () {
    console.log('b')
}, 1000)
setTimeout(function () {
    console.log('c')
})
console.log('d')

jquery Deferred

  • jQ1.5 的变化
  • 使用 jQ Deferred

不要以为所有的网站都是 vue 和 React 开发的

// jQ1.5之前
var ajax = $.ajax({
    url: './data.json',
    success: function () {
        console.log('success 1')
        console.log('success 2')
        console.log('success 3')
    },
    error: function () {
        console.log('error')
    }
})
console.log(ajax)      // 返回一个 Deferred 对象


// jQ1.5之后
var ajax = $.ajax('./data.json')
ajax.done(function () {
    console.log('success a')
}).fail(function () {
    console.log('fail 1')
}).done(function () {
    console.log('success b')
}).fail(function () {
    console.log('fail 2')
}).done(function () {
    console.log('success c')
}).fail(function () {
    console.log('fail 3')
})
console.log(ajax)      // 返回一个 xhr 对象


// jQ1.5之后
// 很像 Promise 的写法
var ajax = $.ajax('./data.json')
ajax.then(function () {
    console.log('success 100')
}, function () {
    console.log('fail 100')
}).then(function () {
    console.log('success 200')
}, function () {
    console.log('fail 200')
}).then(function () {
    console.log('success 300')
}, function () {
    conso3le.log('fail 300')
})

jQ1.5 的变化:

  • 无法改变 JS 异步和单线程的本质
  • 只能从写法上杜绝 callback 这种形式
  • 它是一种语法糖形式,但是解耦了代码
  • 很好的体现:开放封闭原则

使用 jQuery Deferred

var wait = function () {
    var task = function () {
        console.log('执行完成')
    }
    setTimeout(task, 2000)
}
wait()
// 已经封装好的
function waitHandle() {

    var dtd = $.Deferred()          // 创建一个 deffered 对象

    var wait = function (dtd) {     // 要求传入一个 deffered 对象
        var task = function () {
            console.log('执行完成')
            dtd.resolve()           // 表示异步任务已经完成
            // dtd.reject()         // 表示异步任务失败或出错
        }
        setTimeout(task, 1000)
        // wait 返回
        return dtd;                 // 要求返回 deffered 对象
    }

    // 最终返回,这里一点要有返回值
    return wait(dtd)
}

// 使用1
var w = waitHandle();

// w.reject()       可以主动调用

w.then(function () {
    console.log('ok 1')
}, function () {
    console.log('err 1')
}).then(function () {
    console.log('ok 2')
}, function () {
    console.log('err 2')
})


var w = waitHandle()
w.done(function () {
    console.log('success a')
}).fail(function () {
    console.log('fail 1')
}).done(function () {
    console.log('success b')
}).fail(function () {
    console.log('fail 2')
})

总结:

  • 总结,dtd 的 API 可分成两类,用意不同
  • 第一类: dtd.resolve dtd.reject
  • 第二类: dtd.then dtd.done dtd.fail
  • 这两类应该分开,否则后果很严重!
  • 可以在上面代码最后执行 dtd.reject() 试一下后果

使用 dtd.promise():promise 对象只能监听,不能修改(即不能主动调用 reject())

function waitHandle() {
    var dtd = $.Deferred()
    var wait = function (dtd) {
        var task = function () {
            console.log('执行完成')
            dtd.resolve()
        }
        setTimeout(task, 1000)
        // wait 返回
        return dtd.promise()            // 注意返回的是 promise 对象
    }
    // 最终返回
    return wait(dtd)
}

var w = waitHandle()  // promise 对象
$.when(w).then(function () {
    console.log('ok 1')
}, function () {
    console.log('err 1')
})

// 对 w.reject 会直接报错,这里不可以执行    只能被动监听,不能主动修改

总结:

  • 可以 jQuery 1.5 对 ajax 的改变举例
  • 说明如何简单的封装、使用 Deferred
  • 说明 promise 和 Deferred 的区别

要想深入理解它,就需要知道它的身世今生

Promise

  • 基本语法
  • 异常捕获
  • 多个串联
  • Promise.all \ Promise.race
  • Promise 标准

基本语法:

var src1 = 'https://www.baidu.com/img/540%20258_c622d80176946df7f7b8d1997edf57d4.gif'

function loadImg(src) {
    var promise = new Promise(function (resolve, reject) {
        var img = document.createElement('img')
        img.onload = function () {
            resolve(img)
        }
        img.onerror = function () {
            reject('图片加载失败')
        }
        img.src = src
    })
    return promise
}

var result = loadImg(src1)
result.then(function (img) {
    console.log(1, img.width)
    return img
}, function () {
    console.log('error 1')
}).then(function (img) {
    console.log(2, img.height)
})

异常捕获:规定,then 只接受一个参数,最后统一用 catch 捕获异常

var result = loadImg(src)
result.then(function (img) {
    console.log(1, img.width)
    return img
}).then(function (img) {
    console.log(2, img.height)
}).catch(function (ex) {
    // 统一捕获异常
    console.log(ex)
})

多个串联:

var src2 = 'https://www.baidu.com/img/540%20258_c622d80176946df7f7b8d1997edf57d4.gif'

var result1 = loadImg(src1)
var result2 = loadImg(src2)

result1.then(function (img1) {
    console.log('第一个图片加载完成', img1.width)

    return result2  // 重要!!! 返回一个 promise 实例

}).then(function (img2) {
    console.log('第二个图片加载完成', img2.width)
}).catch(function (ex) {
    // 统一捕获异常
    console.log(ex)
})

promise-all-race:

var src2 = 'https://www.baidu.com/img/540%20258_c622d80176946df7f7b8d1997edf57d4.gif'

var result1 = loadImg(src1)
var result2 = loadImg(src2)

// Promise.all 接收一个 Promise 对象的数组
// 待全部完成之后,统一 success
Promise.all([result1, result2]).then(function (datas) {
    // 收到的 datas 是一个数组,依次包含了多个 Promise 返回的内容
    console.log('all', datas[0])
    console.log('all', datas[1])
})

// Promise.race 接收一个包含多个 Promise 对象的数组
// 只要有一个完成,就执行 success
Promise.race([result1, result2]).then(function (data) {
    // data 是最先执行完成的 Promise 的返回值
    console.log('race', data)
})

Promise 标准:

  • 关于 "标准" 的闲谈
  • 状态变化
  • then

关于 "标准" 的闲谈:

  • 任何技术推广使用都需要一套标准来支撑
  • 如 html js css http 等,无规矩不成方圆
  • 任何不符合标准的东西,终将会被用户抛弃
  • 不要挑战标准,不要自造标准

Promise 标准 - 状态变化:

  • 三种状态:pending fulfilled rejected
  • 初始状态是 pending
  • pending 变为 fulfilled ,或者 pending 变为 rejected
  • 状态变化不可逆

Promise 标准 - then:

  • Promise 实例必须实现 then 这个方法
  • then() 必须可以接收两个函数作为参数
  • then() 返回的必须是一个 Promise 实例

总结:

  • 基本语法
  • 如何异常捕获(Error 和 reject 都要考虑)
  • 多个串联 - 链式执行的好处
  • Promise.all 和 Promise.race
  • Promise 标准 - 状态变化,then 函数

async、wait

  • then 只是将 callback 拆分了
  • async/await 是最直接的同步写法
  • 语法

then 只是将 callback 拆分了:

var w = waitHandle();
w.then(function () {
    console.log('ok 1')
}, function () {
    console.log('err 1')
}).then(function () {
    console.log('ok 2')
}, function () {
    console.log('err 2')
})

最直接的同步写法:

import 'babel-polyfill'

function loadImg(src) {
    var promise = new Promise(function (resolve, reject) {
        var img = document.createElement('img')
        img.onload = function () {
            resolve(img)
        }
        img.onerror = function () {
            reject('图片加载失败')
        }
        img.src = src
    })
    return promise
}

const load = async function () {
    const result1 = await loadImg(src1);
    console.log(result1);
    const result2 = await loadImg(src2);
    console.log(result2)
}
load()

用法:

  • 使用 await ,函数必须用 async 标识
  • await 后面跟的是一个 Promise 实例
  • 需要 babel-polyfill

总结:

  • 基本语法
  • 使用了 Promise ,并没有和 Promise 冲突
  • 完全是同步的写法,再也没有回调函数
  • 但是:改变不了 JS 单线程、异步的本质

总结

目标:

  • 什么是单线程,和异步的关系
  • 什么是 event-loop
  • 是否用过 jquery 的 Deferred
  • 目前 js 解决异步的方案有哪些
  • 如果只有 jq 如何解决异步
  • Promise 的基本使用和原理
  • async、wait 的使用(ES2017)(和 Promise 的区别、联系)

什么是单线程,和异步的关系:

  • 单线程就是同时只做一件事,两段 JS 不能同时执行
  • 原因就是为了避免 DOM 渲染的冲突
  • 异步是一种"无奈"的解决方案,虽然有很多问题

什么是 event-loop:

  • 事件轮询,JS 异步的解决方案
  • 什么是异步队列,何时被放入异步队列
  • 轮询的过程

jQuery Deferred:

  • 可以 jQuery 1.5 对 ajax 的改变举例
  • 说明如何简单的封装、使用 Deferred
  • 说明 promise 和 Deferred 的区别

Promise 使用和原理:

  • 基本语法
  • 如何异常捕获
  • 多个串联 - 链式执行的好处
  • Promise.all 和 Promise.race
  • Promise 标准 - 状态变化,then 函数

async/await:

  • 基本语法
  • 使用了 Promise ,并没有和 Promise 冲突
  • 完全是同步的写法,再也没有回调函数
  • 但是:改变不了 JS 单线程、异步的本质

当前异步的解决方案:

  • jQuery Deferred
  • Promise
  • Async/Await
  • Generator

关于 Generator:

  • 原理比较复杂
  • 不是异步的直接替代方式
  • 有更好更简洁的解决方案 async/await
  • koa 也早已"弃暗投明"