赞
踩
JavaScript 是一种单线程编程语言,这意味着同一时间只能完成一件事情。也就是说,JavaScript 引擎只能在单一线程中处理一次语句。
CallBack,setTimeOut,ajax 等都是通过事件循环(event loop)实现的。
主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在”任务队列”中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取”任务队列”,依次执行那些事件所对应的回调函数。
主线程在运行的时候,将产生堆(heap)和栈(stack),栈中的代码会调用各种外部API,它们将在”任务队列”中根据类型不同,分类加入到相关任务队列中,如各种事件等。只要栈中的代码执行完毕,主线程就会去读取”任务队列”,根据任务队列的优先级依次执行那些事件所对应的回调函数。这就是整体的事件循环。
微任务队列中的所有任务都将在宏队列中的任务之前执行。也就是说,事件循环将首先在执行宏队列中的任何回调之前清空微任务队列。
举例:
console.log('Script start'); setTimeout(() => { console.log("setTimeout 1"); }, 0); setTimeout(() => { console.log("setTimeout 2"); }, 0); new Promise ((resolve, reject) => { resolve("Promise 1 resolved"); }) .then(res => console.log(res)) .catch(err => console.log(err)); new Promise ((resolve, reject) => { resolve("Promise 2 resolved"); }) .then(res => console.log(res)) .catch(err => console.log(err)); console.log('Script end');
运行结果是:
Script start
Script end
Promise 1
Promise 2
setTimeout 1
setTimeout 2
通过上述例子可以看到无论宏队列的位置在何方,只要微队列尚未清空,一定会先清空微队列后,在去执行宏队列。下面介绍微队列任务中比较典型的几个API,通过相关举例,让你更深入理解JS的异步机制。
Promise,就是一个对象,用来传递异步操作的消息。
基础用法:
var promise = new Promise(function(resolve, reject) {//异步处理逻辑//处理结束后,调用resolve返回正常内容或调用reject返回异常内容 }) promise.then(function(result){//正常返回执行部分,result是resolve返回内容 }, function(err){//异常返回执行部分,err是reject返回内容 }) .catch(function(reason){//catch效果和写在then的第二个参数里面一样。另外一个作用:在执行resolve的回调时,如果抛出异常了(代码出错了),那么并不过报错卡死JS,而是会进入到这个catch方法中,所以一般用catch替代then的第二个参数 });
用法注意点 - 顺序:
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
运行结果是:
2
1
说明:立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。也就是resolve(1)和console.log(2)是属于同步任务,需要全部执行完同步任务后,再去循环到resolve的then中。
用法注意点 - 状态:
const p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error(‘fail’)), 3000); });const p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000); });const p3 = new Promise(function (resolve, reject) { setTimeout(() => resolve(new Error(‘fail’)), 1000); }); p2 .then(result => console.log(“1:”, result)) .catch(error => console.log(“2:”,error)); p3 .then(result => console.log(“3:”, result)) .catch(error => console.log(“4:”,error));
运行结果是:
3: Error: fail
at setTimeout (async.htm:182)
2: Error: fail
at setTimeout (async.htm:174)
说明:p1是一个 Promise,3 秒之后变为rejected。p2和p3的状态是在 1 秒之后改变,p2 resolve方法返回的是 p1, p3 resolve方法返回的是 抛出异常。但由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。而p3返回的是自身的resolve,所以触发then中指定的回调函数。
用法注意点 - then链的处理:
var p1 = p2 = new Promise(function (resolve){ resolve(100); }); p1.then((value) => { return value*2; }).then((value) => { return value*2; }).then((value) => { console.log("p1的执行结果:",value) }) p2.then((value) => { return value*2; }) p2.then((value) => { return value*2; }) p2.then((value) => { console.log("p2的执行结果:",value)
运行结果是:
p2的执行结果:100
p1的执行结果:400
说明:p2写法中的 then 调用几乎是在同时开始执行的,而且传给每个 then 方法的 value 值都是 100。而p1中写法则采用了方法链的方式将多个 then 方法调用串连在了一起,各函数也会严格按照 resolve → then → then → then 的顺序执行,并且传给每个 then 方法的 value 的值都是前一个promise对象通过 return 返回的值。
用法注意点 - catch的处理:
var p1 = new Promise(function (resolve, reject){ reject("test"); //throw new Error("test"); 效果同reject("test"); //reject(new Error("test")); 效果同reject("test"); resolve("ok"); }); p1 .then(value => console.log("p1 then:", value)) .catch(error => console.log("p1 error:", error)); p2 = new Promise(function (resolve, reject){ resolve("ok"); reject("test"); }); p2 .then(value => console.log("p2 then:", value)) .catch(error => console.log("p2 error:", error));
运行结果是:
p2 then: ok
p1 error: test
说明:Promise 的状态一旦改变,就永久保持该状态,不会再变了。不会即抛异常又会正常resolve。
async基础用法:
async 用于申明一个 function 是异步的,返回的是一个 Promise 对象。
async function testAsync() {
return "hello async";
}
var result = testAsync();
console.log("1:", result);
testAsync().then(result => console.log("2:", result));
async function mytest() {
//"hello async";
}
var result1 = mytest();
console.log("3:", result1);
运行结果是:
1: Promise {<resolved>: “hello async”}
3: Promise {<resolved>: undefined}
2: hello async
说明:async返回的是一个Promise对象,可以用 then 来接收,如果没有返回值的情况下,它会返回 Promise.resolve(undefined),所以在没有 await 的情况下执行 async 函数,它会立即执行,并不会阻塞后面的语句。这和普通返回 Promise 对象的函数并无二致。
await基础用法:
await 只能出现在 async 函数中,用于等待一个异步方法执行完成(实际等的是一个返回值,强调 await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果)。
function getMyInfo() { return Promise.resolve("hello 2019!"); } async function testAsync() { return "hello async"; } async function mytest() { return Promise.reject("hello async"); } async function test() { try { const v1 = await getMyInfo(); console.log("getV1"); const v2 = await testAsync(); console.log("getV2"); const v3 = await mytest(); console.log(v1, v2, v3); } catch (error) { console.log("error:", error); } } test();
运行结果是:
getV1
getV2
error: hello async
说明:await等到的如果是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
放心,这就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。
async/await的优势:
很多情况下,执行下一步操作是需要依赖上一步的返回结果的,如果当嵌套层次较多的时候,(举例3层的时候):
const getRequest = () => {
return promise1().then(result1 => {
//do something
return promise2(result1).then(result2 => {
//do something
return promise3(result1, result2)
})
})
}
从上例可以看到嵌套内容太多。此时如果用async写法,可写成如下:
const getRequest = async () => {
const result1 = await promise1();
const result2 = await promise2(result1);
return promise3(result1, result2);
}
说明:async / await 使你的代码看起来像同步代码,它有效的消除then链,让你的代码更加简明,清晰。
本文由腾讯蓝鲸智云编辑发布,腾讯蓝鲸智云(简称蓝鲸)软件体系是一套基于PaaS的技术解决方案,致力于打造行业领先的一站式自动化运维平台。目前已经推出社区版、企业版,欢迎体验。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。