1、JavaScript(JS) 运行机制
JS 是单线程运行的,也就是两段代码不能同时运行,而是必须逐步地运行,所以在同步代码执行过程中,异步代码是不执行的。只有等同步代码执行结束后,异步代码才会被添加到事件队列中。这里就涉及到执行栈和任务队列:
同步代码是依次存放在执行栈中,遵循LIFO原则;
异步代码存放在任务队列中,任务队列又分宏任务和微任务(微任务执行优先级高于宏任务),遵循FIFO原则;
例如,
function foo(){
console.log('start...');
return bar();
}
function bar(){
console.log('bar...');
}
//这里采用ES6的箭头函数、Promise函数
var promise = new Promise(function(resolve,reject){
console.log('promise...');
resolve();
});
promise.then(()=>console.log('promise resolve...'));
setTimeout(()=>console.log('timeout...'),0);
foo()
console.log('end...');
输出结果:
promise...
start...
bar...
end...
promise resolve...
timeout...
2、异步编程常用实现方式
JavaScript(JS)异步编程常用实现方式有,常用的定时器、ajax
、Promise
、Generator
、async
/await
。
1)定时器
使用setTimeout()
与setInterval()
定时器来实现异步。
例如,
console.log( "a" );
setTimeout(function() {
console.log( "c" )
}, 500 );
setTimeout(function() {
console.log( "d" )
}, 500 );
setTimeout(function() {
console.log( "e" )
}, 500 );
console.log( "b" );
或者
(function(i){// 闭包
var countDown;
// 对按钮进行操作
function change(i) {
var text = "我同意";
if (i > 0) {
text += "(" + i + "秒)";
} else {
$(':button').prop('disabled', false);
// 不再调用
clearInterval(countDown);
}
$(':button').val(text);
};
// 页面加载完毕执行
$(function(){
// 计时器(1秒调用一次)
countDown = setInterval(function () {
change(i);
i--;
}, 1 * 1000);// 1000毫秒
});
})(60);// 60s
2)异步AJAX
XMLHttpRequest
常常用于请求来自远程服务器上的 XML
或 JSON
数据。一个标准的 XMLHttpRequest
对象往往包含多个回调.
例如,
(function(){
var xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
var url = "authorInfo.json";
xmlhttp.onreadystatechange = function(){
if(xmlhttp.readyState==4){
if(xmlhttp.status==200){
console.log(xmlhttp.response);
//异步获取数据后再doSomething
}
}
}
xmlhttp.open('GET',url,true);
xmlhttp.send(null);
})();
3)使用Promise
Promise
是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise
对象。
例如,
const p = function(){
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('Refused the request!');
},0);
})
};
const p2 = function(){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(p())
},0);
})
};
p2().then(val => {
console.info('Status switches to fulfilled');
console.info(val);
}, val => {
console.info('Status switches to reject');
console.info(val);
});
// 输出 "Status switches to reject"
// 输出 "Refused the request!"
4)Generator
Generator 顾名思义是生成器, 使用 function*
语法和一个或多个 yield
表达式以创建一个函数即为生成器,当然它的返回值就是一个迭代器即生成器。
例如,
function* gen(x){
var y = yield x + 2;
return y;
}
Generator语法 function* name(){},
一般*
和函数名中间有个空格,函数体内可通过yield
关键字修饰,需要注意的是,yield
后面的表达式,只有当调用next
方法、内部指针指向该语句时才会执行。
5)async/await
带async
关键字的函数,是声明异步函数,返回值是Promise对象,如果async
关键字函数返回的不是Promise,会自动用Promise.resolve()
包装。await
等待右侧表达式的结果,这个结果是Promise对象或者其他值。如果它等到的不是一个 Promise 对象,那 await
表达式的运算结果就是返回的值。
如果它等到的是一个 Promise 对象,await
就会执行,它会阻塞后面的代码,等着 Promise 对象 resolve
,然后得到 resolve
的值,作为 await
表达式的运算结果。
例如,
function myFun() {
return new Promise(resolve => {
setTimeout(() => resolve("cjavapy"), 2000);
});
}
async function runAsync() {
const result = await myFun();
console.log(result);
}
runAsync();
console.log('end');