《异步和promise》

252 阅读5分钟

一. 什么是异步和同步

  1. 同步:如果能直接拿到结果,那就是同步。

    比如:你去医院窗口挂号,目的是拿到号,你在拿到号之前是不会离开窗口的。总之,不拿到结果就不离开。对JS来说,就是,不执行完这一句代码,就一直停在这,不往下执行。

  2. 异步:如果不能直接拿到结果,就是异步。

    比如:在餐厅门口等位。拿到号码之后,因为前边排队的人太多,不能马上吃饭,也就是不能直接得到结果。可以拿到号去逛街,做点别的事。

    那什么时候才能真正吃饭呢?(拿到结果有两种方式

    • 你可以每十分钟自己去餐厅门口问一下,到几号了。(轮询
    • 你也可以留下电话号码,等轮到你了,餐厅给你打电话通知你可以去吃饭了。(回调
  3. 异步举例:AJAX

    request.send() 发送请求之后,不能直接拿到response。在send后马上打印出response,没有结果。但是,隔几毫秒再打印,就有结果。

    同一个值,一开始拿不到结果,但是过一会就能拿到结果,就是异步。

    那我怎么拿到结果呢?我写一个onreadystatechange函数,放在request对象上,等下载完成以后,浏览器用这个函数。我就拿到了结果。这和我把电话留给餐厅的过程一样:我并不能马上吃饭,那餐厅怎么通知我可以吃饭了呢?我留一个电话号放在那,告诉餐厅,等结果出来了,给我打个电话(回头调用一下我写的这个函数)。

  4. 回调函数 callback

    我自己写的函数,但自己不调用,而是给别人调用,这就是回调。(被别人调用的函数,是回调函数,是个名词。调这个回调函数,也是回调)

    即:你自己写的函数,自己调用了,不是回调。

    写给别人用的函数,就是回调。

    回调,就是回头调用一下,将来的某个时刻,调用一下。

    以AJAX为例,request.onreadystatechange 就是我写给浏览器调用的。因为send之后不能马上拿到结果,所以我写了个函数放在那,告诉浏览器,等结果出来了,你回头记得调用一下我这个函数。

    f1是我自己写的函数,但是我没调用,我把f1作为参数传给了f2,f2调用了f1。这个f2就是别人。f1就是我写给f2用的回调函数。

    回调可以把回调函数当作参数传给别人,把这个函数直接放到别人手里。也可以像request.onreadystatechange那样,把它放到request身上,让浏览器直接从request身上读。也就是说,不一定非要当作参数传给别人。

  5. 异步和回调的关系

    异步是不能直接拿到结果,回调是我写一个函数放在那,等别人调用。

  • 关联:异步需要用回调函数来通知结果。

    让JS留一个函数地址给浏览器(留一个电话号码)。等异步任务完成时,浏览器回头调用一下这个函数地址(打电话)。同时,要把结果作为参数传给这个函数(电话里说可以吃饭了)。

  • 区别:异步想要通知结果不一定要用回调,也可以轮询。只不过回调更常用。回调也不一定只用在异步任务里,也可以用在同步任务里。如arr.forEach()。即:异步和回调只是合作的关系。

二. 怎么区分函数是同步还是异步?

1. 不要把AJAX设为同步

首先,AJAX可以设置为同步的,request.open('GET','/5.josn',false),第三个参数,选择是否异步,默认是异步,fasle表示同步。但是,如果设为同步,在JS得到结果之前,啥都不干,只是等着,会使请求期间页面卡住。

2.

1. setTimeout举例

声明一个摇骰子函数q,在q里有一个setTimeout,一秒之后返回一个0-6的整数。

  • q()里没有写return,也就是return undefined
  • setTimeout里有return,即,真正的结果是由setTimeout返回的。
  • 这两个return属于不同的函数

所以,这是一个异步函数/异步任务。所以,调用q,是拿不到结果的,为undefined。它需要一个回调函数,来通知结果。

我写一个回调函数f1,放在那,把这个函数地址留给q。当q拿到结果时,就回头调用一下f1,同时,在调用f1的时候,把结果作为参数传给f1。

由于函数f1声明之后,只用了一次,所以可以省掉f1这个变量,简化为箭头函数。q(x=>console.log(x))

再由于,x被传进去,又打印出来,所以还可以简化q(console.log)注意:console.log后边并没有括号,我没有调用它,只是把它作为参数传给了别人。

如果参数的个数不一致,就不能这样简化。

即,如果传进去(x,y),但是只打印一个x,就不能这样简化。有一个面试题:

想把字符串数组变成整数。于是在调用map函数时,传了回调函数parseInt,但是结果出错了。因为这个回调是一个简化写法,它相当于:
map方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback函数会被自动传入三个参数:数组元素,元素索引,原数组本身。但是parseInt函数只接受两个参数,第三个可以默认忽略。第一个是要转换的字符串,第二个是进制。所以这样调用时,把'1'当成0进制,0可以忽略,不变,还是1。把'2'当成1进制的数转换,1进制没有2,所以是NAN。后一个同理。所以最好用箭头函数写全: