callback hell 方法的使用
可读性不友好
- function loadImg(src,callback,fail){
- var img = document.createElement('img');
- img.onload = function(){
- callback(img);
- }
- img.onerror = function(){
- fail();
- }
- img.src = src;
- }
- var src = "https://www.baidu.com/img/baidu_jgylogo3.gif";
- loadImg(src,function(img){
- console.log('img width',img.width)
- },function(){
- console.log('faild')
- })
用 promise 去改造
用多个. then 去分批处理
- function loadImg(src){
- return new Promise(function(resolve,reject){
- var img = document.createElement('img');
- img.onload = function(){
- resolve(img);
- }
- img.onerror = function(){
- reject();
- }
- img.src = src;
- })
- }
- var src="https://www.baidu.com/img/baidu_jgylogo3.gif";
- var result = loadImg(src);
- result.then(function(img){
- console.log('img width',img.width)
- },function(){
- console.log('faild')
- })
- result.then(function(img){
- console.log('img height',img.height)
- })
问题解答:
new promise 实例, 而且要传入 return
new promise 时要传入函数, 函数有 resolve reject 函数
成功时执行 resolve() 失败时执行 reject()
.then 监听结果
ES6 常用功能
let 定义变量, 可以重新赋值
const 定义常量, 不可以重新赋值
多行字符串 / 模板变量
解构赋值 (详情见 es6)
块级作用域 (很大的改动)
函数默认参数
箭头函数
块级作用域
- <!-- 在 js 里面 -->
- var obj = {a:100,b:200}
- for (var key in obj) {
- console.log(key,'for in')
- }
- console.log(key) // 100,200
- <!-- 在 es6 -->
- var obj = {a:100,b:200}
- for (let key in obj) {
- console.log(key,'for in')
- }
- console.log(key); // undefine
箭头函数改变 this 指向的问题
- <!-- 用箭头函数, 指向本身函数对象 -->
- function fn(){
- console.log('real',this); // {a:100}
- var arr = [1,2,3];
- arr.map(item=>{
- console.log(this); // {a:100}
- })
- }
- fn.call({a:100}); // call 是强制让 fn 函数对象指向 {a:100}
- <!-- 不用箭头函数, this 指向 window -->
- function fn(){
- console.log('real',this); // {a:100}
- var arr = [1,2,3];
- arr.map(function(item){
- console.log(this); // Windows;
- })
- }
- fn.call({a:100});
原型
描述一下 zepto 是如何使用原型
描述一下 jQuery 如何使用原型
再结合自己的项目经验, 说一个自己开发的列子
原型如何体现他的扩展性
异步
问题:
一. 什么是单线程, 和异步有什么关系
单线程只有一个线程, 同一时间只能做一件事情
- var i,sum = 0;
- for(i=0;i<100000;i++){
- sum+=i;
- }
- console.log(sum);
- <!-- alert 不处理, js 执行和 dom 渲染暂时卡顿 -->
- console.log(1);
- alert('hello');
- console.log(2);
用单线程的原因 - 避免 DOM 渲染的冲突
A. 浏览器需要渲染 DOM
B. JS 可以修改 DOM 结构
C. JS 执行的时候, 浏览器 DOM 渲染会暂停
D. 两段 JS 也不能同时执行 (都修改 DOM 就冲突了)
E. webworder 支持多线程, 但是不能访问 DOM
单线程的解决单线程方案 - 异步
- console.log(100)
- setTimeout(function(){
- console.log(200);
- },1000)
- console.log(300);
- console.log(400);
- // 100
- // 300
- // 400
- // 200 一秒后执行
- console.log(100)
- $.Ajax({
- url:'XXXX',
- success:function(res){
- console.log(res) // 最后执行
- }
- });
- console.log(300)
- cobsole.log(400)
异步的问题:
问题一: 没有按照书写的方式执行, 可读性差
问题二: callback 中不容易模块化 (setTimeout 和 Ajax 里面的函数)
二. 什么是 event-loop 异步的解决方案.
事件轮询, JS 实现异步的具体解决方案
同步代码, 直接执行
异步函数先放在异步队列中; 有 setTimeout 等待时间过后, 才放入异步队列中. Ajax 执行完毕过后放入异步队列
待同步函数执行完毕, 轮询执行异步队列的函数
回答点:
什么是异步队列, 何时被放入异步队列
轮询的过程.
三. 是否用过 jQuery 的 deferred
问题解答:
可以拿 jquery1.5 对 Ajax 的改变举例
说明如何简单的封装, 使用 deferred, 说出它的好处
说明 promise 和 deferred 的区别
jQuery1.5 的变化 --1.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');
- }
- })
jQuery1.5 的变化 --1.5 之后
这样写的好处:
这样对修改封闭, 对扩展开放,
增加一个函数, 不用再修改原来的函数
多人开发, 一人维护一个函数.
测试也只测试新的函数.
对代码管理和维护有好处的
- // 每一个方法都会执行
- var Ajax = $.Ajax('data.json');
- Ajax.done(function(){
- console.log('success 1');
- }).fail(function(){
- console.log('fail 1');
- }).done(function(){
- console.log('success 2');
- }).fail(function(){
- console.log('fail 2');
- }).done(function(){
- console.log('success 3');
- }).fail(function(){
- console.log('fail 3');
- })
- <!-- 与 promise 很相近了 -->
- var Ajax = $.Ajax('data.json')
- Ajax.then(function(){
- console.log('success 1');
- },function(){
- console.log('error 1');
- }).then(function(){
- console.log('success 2');
- },function(){
- console.log('error 2');
- })
无法改变 JS 异步和单线程的本质
只能从写法上杜绝 callback 这种形式
它是一种语法糖形式, 但是解耦了代码
很好的体现: 开放封闭原则
减少了回归测试的成本. 多人开发方便.
使用 jQuery deferred 举例:
- var wait = function(){
- var task = function(){
- console.log('执行完成');
- }
- setTimeout(task,20000)
- }
- wait();
- function waitHandle(){
- // 定义
- var dtd = $.Deferred();
- var wait = function(dtd){
- var task = function(){
- console.log('执行完毕');
- // 成功
- dtd.resolve()
- // 失败
- // dtd.reject()
- }
- setTimeout(task,2000);
- // wait 返回
- return dtd;
- }
- // 最终返回
- return wait(dtd);
- }
- var w = waitHandle();
- w.then(function(){
- console.log('ok 1');
- },function(){
- console.log('err 1');
- })
- w.then(function(){
- console.log('ok 2');
- },function(){
- console.log('err 2');
- })
- w.then(function(){
- console.log('ok 3');
- },function(){
- console.log('err 3');
- })
- w.reject(); // 不会报错
- // 开放封闭原则!!!
总结:
dtd 的 API 可分成两类, 用意不同
第一类: dtd.reolve dtd.reject 主动触发
第二类: dtd.then dtd.done dtd.fail 被动监听
初步引入 promise 概念, 是 jQuery deferred 引入过来的
promise 和 jQuery deferred 的区别:
promise 对象只能被动监听, 不能主动修改, 所以在 w.reject() 会报错.
- function waitHandle(){
- // 定义
- var dtd = $.Deferred();
- var wait = function(dtd){
- var task = function(){
- console.log('执行完毕');
- // 成功
- dtd.resolve()
- // 失败
- // dtd.reject()
- }
- setTimeout(task,2000);
- // wait 返回
- return dtd.promise(); // 这里返回的是 promise 对象
- }
- // 最终返回
- return wait(dtd);
- }
- var w = waitHandle();
- $.when(w).then(function(){
- console.log('ok 1');
- },function(){
- console.log('err 1');
- }).then(function(){
- console.log('ok 2');
- },function(){
- console.log('err 2');
- })
- w.reject(); // 这里就会直接报错.
四. promise 的基本使用和原理
基本语法回顾
1. 异常捕获
- function loadImg(src){
- return new Promise(resolve,reject){
- var img = document.createElement('img');
- img.onload = function(){
- resolve(img);
- }
- img.onerror = function(){
- reject('图片加载失败');
- }
- img.src= src;
- }
- }
- var src="https://wwww.img.address";
- var result = loadimg(src);
- // 规定: then 只接受一个成功的参数, 最后统一用 catch 来捕获错误
- result.then(function(img){
- console.log(1,img.width);
- return img;
- }).then(function(img){
- console.log(2,img.width);
- }).catch(function(ex){
- // 统一捕获异常, 也会捕获到 reject 方法
- console.log(ex);
- })
2. 多个串联
- // 场景: 希望加载完第一个, 再加载第二个
- var src1 = 'img.address';
- var result1 = loadImg(src1);
- var src2 = 'img.address';
- var result2 = loadImg(src2);
- result1.then(function(img1){
- console.log('加载第一章图片',img1.width);
- return result2 // 重要!!!
- }).then(function(img2){
- console.log('加载第二章图片',img2.width);
- }).catch(function(ex){
- console.log(ex);
- })
3. promise.all 和 promise.race
- // Promise.all 接收一个 promise 对象的数组
- // 待全部完成之后, 统一执行 success
- Promise.all([result1,result2]).then(datas=>{
- // 接收到的 datas 是一个数组, 依次包含了多个 promise 返回的内容
- console.log(datas[0]);
- console.log(datas[1]);
- })
- // Promise.race 接收一个包含多个 promise 对象的数组
- // 只要有一个完成, 就执行 success
- Promise.race([result1,result2]).then(data=>{
- // data 是最先执行完成的 promise 对象的返回值
- console.log(datas);
- })
4. promise 标准
关于 "标准" 的闲谈, 标准很重要
任何技术推广使用都需要一套标准来支撑
如 HTML JS CSS http 等, 无规则不成方圆
任何不符合标准的东西, 终将会被用户抛弃
状态变化
三种状态: pending fulfilled rejected
初始状态 pending
pending 变成 fulfilled, 或者 pending 变为 rejected
状态不可逆
then
promise 实例必须实现 then 这个方法
then() 必须可以接收两个函数作参数
then() 返回的必须是一个 promise 实例
问题解答总结:
基本语法复习
如何异常捕获 (error 和 reject 都要考虑)
多个串联 - 链式执行的好处
promise.all 和 promise.race
promise 标准 - 状态变化, then 函数
五. 介绍一下 async/await
是 es7 体验中的.
then 只是将 callback 拆分了
async/await 是最直接的同步写法
- const load = async function(){
- const result1 = await loadImg(src1);
- console.log(result1);
- const result2 = await loadImg(src1);
- console.log(result2);
- }
- load()
语法 (和 promise 的区别和联系)
1. 使用 await, 函数必须用 async 标识
2. await 后面跟的是一个 promise 实例
使用了 promise, 并没有和 promise 冲突
完全是同步的写法, 再也没有回调函数
但是: 改变不了 JS 单线程, 异步的本质
3. 需要安装 babel-polyfill 并, 引入
六. 总结当前 JS 异步解决方案
- jQuery Deferred
- Promise
- async/await
- generator(并不是解决异步的, 原理比较复杂, 忽略)
来源: https://www.cnblogs.com/hibiscus-ben/p/10662245.html