网站建设流程方案,自适应型网站建设哪家好,成都网站建设scyiyou,网站推广的表现方式JS 异步接口调用介绍
Js 单线程模型
JavaScript 语言的一大特点就是单线程#xff0c;也就是说#xff0c;同一个时间只能做一件事。这样设计的方案主要源于其语言特性#xff0c;因为 JavaScript 是浏览器脚本语言#xff0c;它可以操纵 DOM #xff0c;可以渲染动画也就是说同一个时间只能做一件事。这样设计的方案主要源于其语言特性因为 JavaScript 是浏览器脚本语言它可以操纵 DOM 可以渲染动画可以与用户进行互动如果是多线程的话执行顺序无法预知而且操作以哪个线程为准也是个难题。 所以为了避免复杂性从一诞生JavaScript就是单线程这已经成了这门语言的核心特征将来也不会改变。
在 HTML5 时代浏览器为了充分发挥 CPU 性能优势允许 JavaScript 创建多个线程但是即使能额外创建线程这些子线程仍然是受到主线程控制而且不得操作 DOM类似于开辟一个线程来运算复杂性任务运算好了通知主线程运算完毕结果给你这类似于异步的处理方式所以本质上并没有改变 JavaScript 单线程的本质。
任务队列
所有任务可以分成两种一种是 同步任务synchronous另一种是 异步任务asynchronous 。
同步任务指的是在主线程上排队执行的任务只有前一个任务执行完毕才能执行后一个任务。
所以当在执行过程中遇到一些类似于 setTimeout 等异步操作的时候会交给浏览器的其他模块进行处理当到达 setTimeout 指定的延时执行的时间之后回调函数会放入到任务队列之中。
当然一般不同的异步任务的回调函数会放入不同的任务队列之中。等到调用栈中所有任务执行完毕之后接着去执行任务队列之中的回调函数 JavaScript setTimeout(() { console.log(setTimeout) }, 22)//定时22ms后执行setTimeout for (let i 0; i 2;) { i 1 console.log(1) } setTimeout(() { console.log(set2) }, 20)//定时20ms 后执行console.log(set2) //下边循环时间假定是50ms for (let i 0; i 100000000;) { i 99999999 console.log(2) }
大家可以猜测下它的输出内容
输出结果是12set2 setTimeOut
那你可能会好奇为什么结果是这样的呢为了讲清楚这个问题答案我们先要搞清楚两个任务一个是macro-task宏任务一个micro-task微任务
macro-task宏任务
大概包括script(整体代码), setTimeout, setInterval, setImmediateNodeJs, I/O, UI rendering。
micro-task微任务
大概包括: process.nextTickNodeJs, Promise, 来自不同任务源的任务会进入到不同的任务队列。
执行顺序是一开始先 主循环下来先把微队列任务执行完成在执行宏队列的一个任务这个宏任务完成在去执行微队列就这样依次循环执行
有了上边的基础我们接下来在看下下边代码执行过程 JavaScript setTimeout(() { console.log(4) }, 0); //Promise构造方法先执行 new Promise((resolve) { console.log(1); for (var i 0; i 10000000; i) { i 9999999 resolve(); } console.log(2); }).then(() { console.log(5); }); console.log(3);
Promise的构造方法的第一个方法会立即执行的后边then会放到微任务队列里办
第一遍执行完成后会输出
1 2 3
此时的宏队列与微队列是 宏队列 微队列 setTimeout then
一开始js脚本是执行的宏队列这里轮到了微队列会先执行Promise里边的then此时微队列空了接着执行宏队列后续输出的应该是
5 4
最终输出的是
1 2 3 5 4 在看下下边这个复杂的 JavaScript script setTimeout(() { console.log(4) }, 0); new Promise((resolve) { console.log(1); for (var i 0; i 10000000; i) { i 9999999 resolve(); } console.log(2); }).then(() { console.log(5); }); console.log(3); /script script console.log(6) new Promise((resolve) { resolve() }).then(() { console.log(7); }); /script
首先我们可以看到script有2个我们暂且叫它script1 宏队列 微队列 script1 script2
宏队列开始执行script1 的脚本后Promise构造方法会立即执行所以会先打印出来
1 2 3
此时对列为 宏队列 微队列 script2 then5 setTimeout4
script1 宏任务执行完成后接着要把微队列任务都要执行完成接着执行5
1 2 3 5 宏队列 微队列 script2 setTimeout4
执行script2
1 2 3 5 6 宏队列 微队列 setTimeout4 then7
接着执行微队列then
1 2 3 5 6 7
就剩最后一个宏任务setTimeout4了
1 2 3 5 6 7 4 如果现在大家搞清楚了js 的异步多少会有一些了解 接下来介绍下async 与 await
async
async 函数是使用async关键字声明的函数。async 函数是 AsyncFunction 构造函数的实例并且其中允许使用 await 关键字。async 和 await 关键字让我们可以用一种更简洁的方式写出基于 Promise 的异步行为而无需刻意地链式调用 promise。 JavaScript function resolveAfter2Seconds() { return new Promise(resolve { setTimeout(() { resolve(resolved); }, 2000); }); } async function asyncCall() { console.log(calling); const result await resolveAfter2Seconds(); console.log(result); // Expected output: resolved } asyncCall();
我们来分析下这个代码 宏队列 微队列 asyncCall()
执行asyncCall()
输出calling 宏队列 微队列 resolveAfter2Seconds() 宏队列 微队列 2s以后setTimeout 宏队列 微队列 console.log(result)
最终输出结果
Calling
#2秒以后输出
resolve Promise
Promise 对象用于表示一个异步操作的最终完成或失败及其结果值。
一个 Promise 必然处于以下几种状态之一
待定pending初始状态既没有被兑现也没有被拒绝。
已兑现fulfilled意味着操作成功完成。
已拒绝rejected意味着操作失败。
Promise.reject(reason)
返回一个状态为已拒绝的 Promise 对象并将给定的失败信息传递给对应的处理函数。
Promise.resolve(value)
返回一个状态由给定 value 决定的 Promise 对象。如果该值是 thenable即带有 then 方法的对象返回的 Promise 对象的最终状态由 then 方法执行结果决定否则返回的 Promise 对象状态为已兑现并且将该 value 传递给对应的 then 方法。
await
await 操作符用于等待一个 Promise 兑现并获取它兑现之后的值。它只能在异步函数或者模块顶层中使用。
当函数执行到 await 时被等待的表达式会立即执行所有依赖该表达式的值的代码会被暂停并推送进微任务队列microtask queue。然后主线程被释放出来用于事件循环中的下一个任务。即使等待的值是已经敲定的 promise 或不是 promise也会发生这种情况。例如考虑以下代码 JavaScript async function foo(name) { console.log(name, start); await console.log(name, middle); console.log(name, end); } foo(First); foo(Second); // First start // First middle // Second start // Second middle // First end // Second end
可以转化成 JavaScript function foo(name) { return new Promise((resolve) { console.log(name, start); resolve(console.log(name, middle)); }).then(() { console.log(name, end); }); }
可以看下加黄色的字体await可以转成 Promise.then会被放到微任务队列 JavaScript let i 0; queueMicrotask(function test() { i; console.log(microtask, i); if (i 3) { queueMicrotask(test); } }); (async () { console.log(async function start); for (let i 1; i 3; i) { await null; console.log(async function resume, i); } await null; console.log(async function end); })(); queueMicrotask(() { console.log(queueMicrotask() after calling async function); }); console.log(script sync part end); //async function start //script sync part end //microtask 1 //async function resume 1 //queueMicrotask() after calling async function //microtask 2 //sync function resume 2 //microtask 3 //async function end