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');