将同步调用转成异步调用?

有时候同步调用和异步调用同时存在的时候,难免出现混乱。

来看个栗子:

function loadReady(callback){
	var readyState = document.readyState;
	if(readyState ==='complete'||readyState==='interactive'){
		typeof callback === 'function' && callback();
	}else{
		window.addEventListener('DOMContentLoaded', callback);
	}
}

loadReady(function(){
	console.log('DOM已经加载解析完成了!');
});

console.log('准备开始....'); 

 结果:

 

这不是我们想要的,我们希望是先打印“准备开始....”,可是事与愿违。
上面的执行结果是因为回调函数被提前执行了,换句话说,回调函数被当成同步函数执行了。

怎么修改一下执行流程呢?
可以这么修改:

function loadReady(callback){
	var readyState = document.readyState;
	if(readyState ==='complete'||readyState==='interactive'){
		typeof callback === 'function' && setTimeout(callback, 0);
	}else{
		window.addEventListener('DOMContentLoaded', callback);
	}
}

loadReady(function(){
	console.log('DOM已经加载解析完成了!');
});

console.log('准备开始....'); 

  

修改之后的代码中,callback被当做setTimeout的回调函数执行了,setTimeout的第二个参数虽然是0,但是却是在任务消息队列中等待执行的,具体可以看《JS运行机制之 Event Loop 的思考》。

     所以,有时为了避免混乱,适当把回调函数的同步调用转成异步调用是很有必要的,其实就不应该对异步回调函数进行同步调用。对于这个问题,可以看Effective JavaScript 详细介绍---


1、绝对不能对异步回调函数(即使在数据已经就绪)进行同步调用。
2、如果对异步回调函数进行同步调用的话,处理顺序可能会与预期不符,可能带来意料之外的后果。
3、对异步回调函数进行同步调用,还可能导致栈溢出或异常处理错乱等问题。
4、如果想在将来某时刻调用异步回调函数的话,可以使用 setTimeout 等异步API。”

 

看看promise的调用方式
为了避免同步调用和异步调用引起的混乱,promise规定promise只能使用异步的调用方式。

看看promise改写上面的代码:

function onReadyPromise() {
    return new Promise(function (resolve, reject) {
        var readyState = document.readyState;
        if (readyState === 'interactive' || readyState === 'complete') {
            resolve();
        } else {
            window.addEventListener('DOMContentLoaded', resolve);
        }
    });
}
onReadyPromise().then(function () {
    console.log('DOM已经加载解析完成了!');  //异步,执行了resolve()之后就异步的执行.then()里面的回调函数
});
console.log('准备开始....'); 

  

posted @ 2017-05-14 09:59  leaf+  阅读(2026)  评论(0编辑  收藏  举报