同步 异步 回调地狱 Promise

前言

在整个html中,加载有多种,js加载,css样式,img,音频、视频,但是只有js文件是阻塞式同步加载,其他全部是异步加载。

  • 同步:JavaScript 是一门单线程语言,同一时间只能执行一个任务,按照代码顺序,从上往下执行,代码执行是同步并且阻塞的。
  • 异步:不管主线程有没有在执行,重新启用了一个线程来操作;只要有一定时间去触发的都是异步过程(比如 setTimeout()、setInterval()、requestAnimationFrame() 、onload 、onerror等)。外部样式表、图片、音频、视频都属于异步加载。
<body>
    <div id="div0"></div>
    <img src="./img/2-.jpg">
    <script>
        var img=document.querySelector("img");
        //因为图片属于异步加载,执行console.log时,图片还没有加载完成,所以width是0
        console.log(img.width);//0
    </script>
</body>

js 中的事件属于异步,只有当触发时才会执行。

document.onclick=function(){
    console.log("aaa");
}

引入<script>标签时

  • script 标签中,加载 js 文件的方式是阻塞式同步,即每一个js是加载完成后,并且执行完成才会去加载执行下一个标签
  • async,表示 js 将会由阻塞式同步变成异步加载
  • defer,当页面已完成加载后,才会执行该 js 脚本,相当于window.onload
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!--默认引入js文件时,表示先加载a.js,并a.js全部加载执行完毕,才会加载b.js-->
    <!--如果a.js文件太大,则会造成页面阻塞,导致下面的图片、css样式等无法加载-->
    <script src="./js/a.js"></script>
    <script src="./js/b.js"></script>
    <link rel="stylesheet" href="./css/style.css">


    <!-- async异步  script 将会由阻塞式同步变成异步加载 -->
    <script src="./js/a.js" async></script>
	<!--defer 当页面已完成加载后,才会执行该 js 脚本-->
    <script src="./js/b.js" async defer></script>
</head>

回调地狱

当希望事件A执行完成后去执行事件B,事件B执行完成后执行事件C,事件C执行完成后执行事件D…,以此类推,这样的操作如果从上到下依次写出来是下面这样的,代码一层一层嵌套,看起来很庞大。这种结构的代码,则被称之为回调地狱。

var base = 100;
console.log(base);//第一次打印
var img = new Image();
img.src = "./img/3-.jpg";
img.onload = function () {
    base += img.width;
    console.log(base);//第三次打印
    var img1 = new Image();
    img1.src = "./img/4-.jpg";
    img1.onload = function () {
        base += img1.width;
        console.log(base);//第四次打印
        var img2 = new Image();
        img2.src = "./img/5-.jpg";
        img2.onload = function () {
            base += img2.width;
            console.log(base);//第五次打印
            // 回调地狱
        }
    }
}
console.log(base);//第二次打印

为了有一个好的编码习惯,我们规定,所有的代码都必须写在函数中,上面的代码经过优化后如下:
为了更好的解决回调地狱的问题,我们可以使用 Promise(接着往下看)。

var base=100;
init();
function init(){
    var img=new Image();
    img.src="./img/3-.jpg";
    img.onload=imgloadHandler;
}

function imgloadHandler(){
    base+=this.width;
    console.log(base);
    if(this.src.indexOf("3-.jpg")>-1){
        this.src="./img/4-.jpg";
    }else if(this.src.indexOf("4-.jpg")>-1){
        this.src="./img/5-.jpg";
    }
}

Promise

  • Promise 是一个对象,主要用于异步操作。
  • Promise 参数中接受两个函数 resolve 和 reject 作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve 函数;当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject 函数。
  • Promise 有三个状态:Pending-进行中、Fulfilled-成功、Rejected-失败。
  • Promise 对象的状态改变,只有两种可能:从pending变为fulfilled、从pending变为rejected。这两种情况只要发生,状态就凝固了,不会再变了。
let p=new Promise(function(resolve,reject){
	// resolve是成功时要执行的回调函数
    // reject是失败时要执行的回调函数
    let img =new Image();
    img.src="./img/0107/2-.jpg";
    img.onload=function(){
        resolve(img);
    }
    img.onerror=function(){
        reject(img.src+"地址错误");
    }
})

//then里面可以直接写两个参数,第一个参数对应resolve,第二个参数对应reject
p.then(function(img){
    console.log(img)
},function(msg){
    console.log(msg)
})

//也可以将reject对应的操作写成.catch()
p.then(function(img){
	// resolve调用
    console.log(img)
}).catch(function(msg){
	 // reject调用
    console.log(msg)
})
  • 想要某个函数拥有promise功能,只需让其返回一个promise即可。
function loadImage(src){
    return new Promise(function(resolve,reject){
        let img=new Image();
        img.src=src;
        img.onload=function(){
            resolve(img);
        }
        img.onerror=function(){
            reject("地址错误");
        }
    })
}

loadImage("./img/0107/2-.jpg").then(function(img){
    console.log(img) //<img src="./img/0107/2-.jpg">
}).catch(function(msg){
    console.log(msg)
})
  • then 方法中返回一个 Promise 对象,它可以链式调用(如果在这个then中没有返回任何内容时,就会将原promise的实例对象返回)。
  • 一旦执行 resolve 或者 reject 后,将不会再继续触发这些函数。
  • 链接调用中,只要有一个 then 方法出现错误,则只执行一次 reject。
function loadImage(src){
    return new Promise(function(resolve,reject){
        let img=new Image();
        img.src=src;
        img.onload=function(){
            resolve(img);
            resolve(img);//不执行
        }
        img.onerror=function(){
            reject("地址错误");
        }
    })
}

var sum=100;
loadImage("./img/3-.jpg").then(function(img){
    sum+=img.width;
    return  loadImage("./img/4-.jpg")
}).then(function(img){
    sum+=img.width;
    return  loadImage("./img/5-.jpg")
}).then(function(img){
    sum+=img.width;
    return  loadImage("./img/6-.jpg")
}).then(function(img){
    sum+=img.width;
    return  loadImage("./img/7-.jpg")
}).then(function(img){
    sum+=img.width;
    console.log(sum); //打印出结果 
}).catch(function(msg){
   console.log(msg);
})

Promise 的实现原理(待完善):

class Promise_1{
    resolve;
    reject;
    state="pending";
    constructor(fn){
        this.fn=fn;
    }
    then(_resolve,_reject){
        this.resolve=_resolve;
        this.reject=_reject;
        //因为then方法为异步,这里使用setTimeout来执行
        this.ids=setTimeout(()=>this.callfn(),0);
        //返回Promise对象,进行链式调用
        return this;
    }
    catch(_reject){
        this.reject=_reject;
        this.ids=setTimeout(()=>this.callfn(),0);
    }
    callfn(){
        clearTimeout(this.ids);
        if(this.state!=="pending") return;
        this.fn(this.resolve,this.reject);
        //临时用来区分状态
        this.state="finished"
    }
}
 function loadImage(resolve,reject){
    var img=new Image();
    img.src="./img/3-.jpg";
    img.onload=function(){
        resolve(img);
    }
    img.onerror=function(){
        reject("错误的");
    }
}


function resolve(img){
    console.log(img);
}
function reject(msg){
    console.log(msg);
}
var p=new Promise_1(loadImage);
p.then(resolve,reject);
// p.then(resolve).catch(reject);

Promise 的实现原理(完善一):

class Promise1{
	status="pending"
	constructor(fn){
		fn(this.resolve.bind(this),this.reject.bind(this));
	}
	resolve(res){
		if(this.status!=="pending") return;
		let ids = setTimeout(function(){
			this.setVal("resolve",res);
			clearTimeout(ids);
		}.bind(this),0)
	}
	reject(err){
		if(this.status!=="pending") return;
		let ids = setTimeout(function(){
			this.setVal("reject",err);
			clearTimeout(ids);
		}.bind(this),0)
	}
	then(_resolve,_reject){
		this._resolve=_resolve;
		this._reject=_reject;
		return this;
	}
	catch(_reject){
		this._reject=_reject;
	}
	setVal(_status,arg){
		this.status=_status;
		if(_status==="resolve" && this._resolve){
			this._resolve(arg)
		}else if(_status==="reject" && this._reject){
			this._reject(arg)
		}
	}
}

Promise用法

如果then中没有返回任何内容时,就会将原promise的实例对象返回

function loadImage(src){
    return new Promise(function(resolve,reject){
        let img=new Image();
        img.src=src;
        img.onload=function(){
            resolve(img);
        }
        img.onerror=function(){
            reject("地址错误");
        }
    })
}

loadImage("./img/3-.jpg").then(function(img){
    console.log("bbb");//bbb
    console.log(img);//<img src="./img/3-.jpg">
    // 如果在这个then中没有返回任何内容时,就会将原promise的实例对象返回
}).then(function(img){
    console.log("aaa");//aaa
    //因为原promise实例没有再执行resolve,所以就没有得到参数
    console.log(img);//undefined
})

loadImage("./img/3-.jpg").then(function(img){
	console.log(img);//<img src="./img/3-.jpg">
	return loadImage("./img/4-.jpg");
	// 因为返回了一个新的promise对象,因此下面的then触发新对象中resolve,所以还会有返回结果
}).then(function(img){
	console.log(img);//<img src="./img/4-.jpg">
})

Promise.all ( )

  • 是静态方法,这个方法返回一个新的promise对象;
  • Promise.all方法常被用于处理多个promise对象的状态集合;
  • 被带入的参数以逐一异步的方式执行,在所有的 promise 都 resolve 时才会调用 .then 中的成功回调。
let arr=[];
for(let i=3;i<80;i++){
    arr.push(loadImage(`./img/${i}-.jpg`));
}

// all在这里是可以将所有的promise对象,以逐一异步执行的方式全部执行完成
Promise.all(arr).then(function(list){
    // list是所有promise对象的then中resolve函数的参数集合
    list.forEach(item=>console.log(item.src));
}).catch(function(err){
	//如果上面有一个错误,则只返回该错误信息
})

function loadImage(src){
    return new Promise(function(resolve,reject){
        let img=new Image();
        img.src=src;
        img.onload=function(){
            resolve(img);
        }
        img.onerror=function(){
            reject("地址错误");
        }
    })
}

Promise.race ( )

当参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数返回该promise对象

let arr=[];
for(let i=3;i<80;i++){
    arr.push(loadImage(`./img/${i}-.jpg`));
}

Promise.race(arr).then(function(img){
    console.log(img);
    // 上述所有Promise谁先把执行resolve,就显示该resolve带回来的参数
})

function loadImage(src){
    return new Promise(function(resolve,reject){
        let img=new Image();
        img.src=src;
        img.onload=function(){
            resolve(img);
        }
        img.onerror=function(){
            reject("地址错误");
        }
    })
}

Promise.resolve ( )

如果你不知道一个值是否是Promise对象,使用Promise.resolve(value) 来返回一个Promise对象,这样就能将该 value 以 Promise 对象形式使用。

Promise.resolve(10).then(function(num){
    console.log(num);
})

//相当于
var p=new Promise(function(resolve,reject){
    resolve(10);
})
p.then(function(num){
    console.log(num);
})

Promise.reject ( )

返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法

Promise.reject("错误的").catch(function(msg){
   console.log(msg)
});

//相当于
var p=new Promise(function(resolve,reject){
    reject("错误的");
})
p.catch(function(msg){
    console.log(msg);
})

Promise的同步与异步

promise中写在 then 中函数里的内容或者 catch 函数里的内容都是异步的

console.log("aaa");//第一次执行
// var a=0;
let p=new Promise(function(resolve,reject){
    console.log("ccc");//这是同步的,第二次执行
    resolve(10);
});
p.then(function(num){
    console.log("ddd");//这是异步的,第四次执行
    console.log(num);//10,第五次执行
    // 异步,没有onload,他本身也是异步过程
    // a=num;
});
console.log("bbb");//是同步的,第三次执行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值