同步与异步

同步就是能直接拿到结果,比如在医院挂号室,拿到号才能离开窗口。

异步就是不能直接拿到结果,比如在餐厅等餐时还可以去逛街。你可以每10分钟去餐厅问一下(轮询),你也可以扫码用微信接收通知(回调)

回调

异步与回调的关系:

异步任务需要在得到结果时通知JS来拿结果

怎么通知呢?

可以让JS留一个函数地址(电话号码)给浏览器

异步任务完成时浏览器调用该函数地址即可(拨打电话)

同时把结果作为参数传给该函数(电话里说可以来吃了)

这个函数是我写给浏览器调用的,所以是回调函数

注意:

异步不一定只用回调可以用轮询,回调不一定只在异步任务里面,也可以在同步任务里面。

  1. function f1() {
  2. console.log("我是回调")
  3. }
  4. function f2(fn) {
  5. console.log("我是函数不是回调")
  6. fn()
  7. }
  8. f2(f1)

上面的f1没有被调用,但是f1传给了f2 ,f2调用了f1f1就是写给f2调用的函数。那么f1就是回调。

判断同步与异步(初级)

如果一个函数的返回值处于以下几个内部那么就是异步函数:

  1. setTimeout()
  2. AJAX
  3. AddEventListener()

绝对不能让AJAX改成同步的。

  1. function rollTheDice(fn) {
  2. setTimeout(() => {
  3. fn(parseInt(Math.random() * 6) + 1)
  4. }, 1000) // 随机返回1到6的数字
  5. }
  6. rollTheDice((x) => {
  7. console.log(x)
  8. })

方法一:回调接受2个参数

  1. fs.readFile('./text',(error ,data)=>{
  2. if(error){ console.log('失败');return}
  3. console.log(data.toString())
  4. })

方法二:用2个回调

  1. ajax('get','/1.json',data=>{},error=>{})
  1. ajax('get','/1.json',{
  2. success:()=>{}.fail:()=>{}
  3. })

回调的问题

  1. 不够规范,名称五花八门
  2. 容易出现回调地狱,回调地狱就是我们异步任务中嵌套异步任务一层一层的,导致我们的代码臃肿,而promise链式调用解决这种代码问题。
  3. 很难出现错误处理
  1. getUser((user) => {
  2. getGroups(user, (groups) => {
  3. groups.forEach((g) => {
  4. g.filter((x) => x.ownerId === user.Id).forEach((x) =>
  5. console.log(x)
  6. )
  7. })
  8. })
  9. })

Promise

Promise 对象是由关键字 new 及其构造函数来创建的。该构造函数会把一个叫做“处理器函数”(executor function)的函数作为它的参数。这个“处理器函数”接受两个函数——resolvereject ——作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve 函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject 函数。

  1. return new Promsie((resolve,reject)=>{...})

实例

  1. let myFirstPromise = new Promise(function(resolve, reject){
  2. //当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
  3. //在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
  4. setTimeout(function(){
  5. resolve("成功!"); //代码正常执行!
  6. }, 250);
  7. });
  8. myFirstPromise.then(function(successMessage){
  9. //successMessage的值是上面调用resolve(...)方法传入的值.
  10. //successMessage参数不一定非要是字符串类型,这里只是举个例子
  11. console.log("Yay! " + successMessage);
  12. });
  • return new Promise((resolve,rejec)=>{...})
  • 任务成功则调用resolve(result)
  • 任务失败则调用reject(error)
  • resolvereject会再去调用成功和失败函数
  • 使用.then(success, fail) 传入成功和失败函数
  1. ajax = (method, url, options) => {
  2. return new Promise((resolve, reject) => {
  3. const { success, fail } = options
  4. const request27 = newXMLHttpRequest()
  5. request.open(method, url)
  6. request.onreadystatechange = () => {
  7. if (request.readyState === 4) {
  8. //成功就调用resolve, 失败就调用reject
  9. if (request.status < 400) {
  10. resolve.call(null, request.response)
  11. } else if (request.status >= 400) {
  12. reject.call(null, request)
  13. }
  14. }
  15. }
  16. request.send()
  17. })
  18. }

但是封装的JAXA不能POST ;不能添加状态码

你对 Promise 的了解?

  • Promise 不是前端发明的
  • Promise 是目前前端解决异步问题的统一方案
  • window.Promise 是一个全局函数,可以用来构造 Promise 对象
  • 使用 return new Promise((resolve, reject)=> {}) 就可以构造一个 Promise 对象
  • 构造出来的 Promise 对象含有一个 .then() 函数属性

.then() 与.catch()

.then()

  • then方法提供一个供自定义的回调函数,若传入非函数,则会忽略当前then方法。
  • 回调函数中会把上一个then中返回的值当做参数值供当前then方法调用。
  • then方法执行完毕后需要返回一个新的值给下一个then调用(没有返回值默认使用undefined)。
  • 每个then只可能使用前一个then的返回值。

.catch()

在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。

也就是说当函数里面出现错误是 JS不会停止运行,而是将错误传给.catch()。然后继续执行口面的回调。

  1. function getNumber() {
  2. var p = new Promise(function (resolve, reject) {
  3. //做一些异步操作
  4. setTimeout(function () {
  5. var num = Math.ceil(Math.random() * 10) //生成1-10的随机数
  6. if (num <= 5) {
  7. resolve(num)
  8. } else {
  9. reject("数字太大了")
  10. }
  11. }, 1000)
  12. })
  13. return p
  14. }
  15. getNumber()
  16. .then((data) => {
  17. console.log("成功了")
  18. console.log(data)
  19. })
  20. .catch((data) => {
  21. console.log("失败了,错误传过来,继续执行")
  22. console.log(data)
  23. })
  24. .then(() => {
  25. console.log("绕开了,继续执行")
  26. })

.all

谁跑的慢,以谁为准执行回调。all接收一个数组参数,里面的值最终都算返回Promise对象

Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。看下面的例子:

  1. let Promise1 = new Promise(function(resolve, reject){})
  2. let Promise2 = new Promise(function(resolve, reject){})
  3. let Promise3 = new Promise(function(resolve, reject){})
  4. let p = Promise.all([Promise1, Promise2, Promise3])
  5. p.then(funciton(){
  6. // 三个都成功则成功
  7. }, function(){
  8. // 只要有失败,则失败
  9. })

有了all,你就可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据,是不是很酷?有一个场景是很适合用这个的,一些游戏类的素材比较多的应用,打开网页时,预先加载需要用到的各种资源如图片、flash以及各种静态文件。所有的都加载完后,我们再进行页面的初始化。

.race

谁跑的快,以谁为准执行回调

race的使用场景:比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作,代码如下:

  1. //请求某个图片资源
  2. function requestImg(){
  3. var p = new Promise((resolve, reject) => {
  4. var img = new Image();
  5. img.onload = function(){
  6. resolve(img);
  7. }
  8. img.src = '图片的路径';
  9. });
  10. return p;
  11. }
  12. //延时函数,用于给请求计时
  13. function timeout(){
  14. var p = new Promise((resolve, reject) => {
  15. setTimeout(() => {
  16. reject('图片请求超时');
  17. }, 5000);
  18. });
  19. return p;
  20. }
  21. Promise.race([requestImg(), timeout()])
  22. .then((data) =>{
  23. console.log(data);
  24. })
  25. .catch((err) => {
  26. console.log(err);
  27. });

requestImg函数会异步请求一张图片,我把地址写为”图片的路径”,所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。我们把这两个返回Promise对象的函数放进race,于是他俩就会赛跑,如果5秒之内图片请求成功了,那么遍进入then方法,执行正常的流程。如果5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息。

由此可见:Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了。

jQuery.ajax

jQuery.ajax中文文档

type

类型: String

请求方式 (“POST” 或 “GET”), 默认为 “GET”。注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。

url

类型: String

发送请求的地址 (默认: 当前页面地址)。

username

类型: String

于响应HTTP访问认证请求的用户名

  1. // 发出请求后立即分配处理程序,
  2. // 并记住此请求的jqxhr对象
  3. var jqxhr = $.ajax( "example.php" )
  4. .done(function() { alert("success"); })
  5. .fail(function() { alert("error"); })
  6. .always(function() { alert("complete"); });
  7. // perform other work here ...
  8. // 为上述请求设置另一个完成功能
  9. jqxhr.always(function() { alert("second complete"); });

axios

axios中文文档

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

执行 GET 请求

  1. // 为给定 ID 的 user 创建请求
  2. axios.get('/user?ID=12345')
  3. .then(function (response) {
  4. console.log(response);
  5. })
  6. .catch(function (error) {
  7. console.log(error);
  8. });
  9. // 上面的请求也可以这样做
  10. axios.get('/user', {
  11. params: {
  12. ID: 12345
  13. }
  14. })
  15. .then(function (response) {
  16. console.log(response);
  17. })
  18. .catch(function (error) {
  19. console.log(error);
  20. });

执行 POST 请求

  1. axios.post('/user', {
  2. firstName: 'Fred',
  3. lastName: 'Flintstone'
  4. })
  5. .then(function (response) {
  6. console.log(response);
  7. })
  8. .catch(function (error) {
  9. console.log(error);
  10. });

执行多个并发请求

  1. function getUserAccount() {
  2. return axios.get('/user/12345');
  3. }
  4. function getUserPermissions() {
  5. return axios.get('/user/12345/permissions');
  6. }
  7. axios.all([getUserAccount(), getUserPermissions()])
  8. .then(axios.spread(function (acct, perms) {
  9. // 两个请求现在都执行完成
  10. }));