JS进阶 - 异步
同步与异步
同步,等待结果
异步,不等待结果
注意,异步和回调往往伴随出现,但是两者没有直接关系。
1 | // 同步的 sleep |
以上是同步的代码。
1 | // 异步的 sleep |
console.log() 不会等待上一句执行完,比较 setTimeout 我们可以看到,三秒钟内 JS 什么都不需要做,而同步代码中需要不停计时。
setTimeout 是给浏览器设置的一个闹钟,JS 需要做的只是设置闹钟而不是自己成为闹钟。
经典例子
1 | document.getElementsByTagName('img')[0].width // 宽度为 0 |
第一次加载页面的时候是拿不到图片宽度的,因为 JS 不关心浏览器加载图片的结果,它会在请求过程中获取宽度值,也就是 0.
我们需要改写成加载完成图片时拿到宽高数据:
1 | document.getElementsByTagName('img')[0].onload = function (){ |
常见的异步,监听事件。
我们再来看一个例子:
1 | let liList = document.querySelectorAll('li') |
经典的前端小题目,如果给你六个 li,它会一直输出6,因为变量提升,这个 i 会提升到全局当中去,JS 会瞬间执行完整个过程,当你 onclick 的时候,已经晚了。
注意,虽然六项,但是第六项 i=5 的时候仍然会比较并且 +1,所以最终是 6。
浏览器并没有等待代码执行,直接进入下一个循环并得出结果。
把 var i 改成 let 就可以破解:https://zhuanlan.zhihu.com/p/28140450
1 | let liList = document.querySelectorAll('li') |
AJAX 中的异步
1 | let request = $.ajax({ |
以上是一个同步的 ajax 代码,ajax 中不应该出现此类代码,因为这个请求会一直占用资源。
我们来改写一个:
1 | $.ajax({ |
这段异步代码告诉浏览器,如果数据传过来了,麻烦你执行一个函数,把内容传给我。
异步的形式
如何拿到异步代码的结果?比如说我们请求一个网络资源,我们使用 ajax,那么怎么获得资源内容呢?
一般有两种方式拿到异步结果:
- 傻逼方法:轮询(定期查看结果是否达到)
1 | function buyApple(){ |
- 正规方法:回调(给一个函数)
1 | function buyApple(fn){ |
回调的形式
回调得告诉你是成功还是失败,随便你怎么告知。
- Node.js 的 error-first 形式:
1 | fs.readFile('./1.txt', (error, content)=>{ |
- jQuery 的 success / error 形式
1 | $.ajax({ |
- jQuery 的 done / fail /always 形式(追加传参,参考柯里化)
1 | $.ajax({ |
- Promise 的 then 形式
1 | $.ajax({ |
我们看到大家对回调报错写法不一,所以我们有了 Promise 规范,你所有的异步操作发出之后,给我暴露一个 API,一个对象,这个对象必须有一个函数,也就是 then。then 必须要有两个回调,成功回调,失败回调。最后那个 then 是对成功操作的加料操作。
如何处理异常?
- 如何使用多个 success 函数?
- 在有多个成功回调的情况下,如何处理异常?
promise.all()
与 promise.race()
自己返回 Promise
1 | function ajax(){ |
Promise 深入阅读:http://www.cnblogs.com/hustskyking/p/promise.html
Promise/A+ 规范:https://segmentfault.com/a/1190000002452115
async / await
1 | function buyFruit(){ |