JavaScript中的如何解决异步请求(面试常出现的异步代码问题)

JavaScript中的如何解决异步请求

想必有过项目经验的小伙伴,都对异步请求不陌生。今天小编就带大家来说说什么是异步请求以及如何解决?

一、常见的异步请求方法(数据请求)

1.XMLHttpRequest js异步请求

2.$.ajax jquery异步方法

3.axios (很多公司使用的异步请求库
  Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
  官方文档:https://www.kancloud.cn/yunye/axios/234845

4.promise是es6里用来解决回调地狱的方案,主要作用是让代码换了个书写形式,由异步形式换成可读性更好的“同步”形式。

5.es7中的async,await同理

  • async 表示这是一个async函数,await只能用在这个函数里面。
  • await 表示在这里等待promise返回结果了,再继续执行。
  • await 后面跟着的应该是一个promise对象(当然,其他返回值也没关系,只是会立即执行,不过那样就没有意义了…)

6.fetch号称是AJAX的替代品,是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch的代码结构比起ajax简单多了,参数有点像jQuery ajax。但是,一定记住fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。

二、解决异步请求的发展及优缺点

  1. 回调函数(callback)
 setTimeout(() => {
            console.log('settimeout');
        }, 1000);
        console.log(1);

        
        $.ajax({
            url:'',
            success(){
                $.ajax({
                    url:'',
                    success(){
                        $.ajax({
                            url:'..'
                        })
                    }
                })
            }
        })

        

试想,如果再多几个异步函数,代码整体的维护性,可读性都变的极差,如果出了bug,修复的排查过程也变的极为困难,这个便是所谓的 回调函数地狱

  优点: 解决了同步问题(解决了  只要有一个任务耗时间很长,后面的任务必须排队进行执行,会延迟严重。)
  缺点:回调地狱,不能用 try catch 捕获错误,不能return
  1. Promise

Promise是解决异步回调的ES语法的标准,通常用来解决异步嵌套和多异步同时完成回调等问题。我的理解就是:在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了。

Promise是一个构造函数,相当于一个容器,把异步代码包裹在里面,promise有三个状态(pending(进行中)、fulfilled(已成功)和rejected(已失败)),初始化为pending,当异步请求成功后调用resolve函数,状态从pending—>fulfilled,失败的时候调用reject,状态从pending—>rejected。状态不可逆。

Promise的几种方法:

        (1)、Promise.prototype.then()
        then方法是定义在原型对象Promise.prototype上的,他的作用是为Promise实例添加状态 改变是的回调函数。
        then方法的第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。为宏任务中的微任务。
        (2)、Promise.prototype.catch()
        此方法是.then(null,rejection)的别名,用于指定错误发生时的回调函数。
        (3)、Promise.all()
        此方法用于将多个Promise实例包装成一个新的promise实例。
        (4)、Promise.race()
        也是将多个Promise实例包装成一个新的promise实例。
        (5)、Promise.resolve()
        将现有对象转换为Promise对象
        (6)、Promise.reject()
        Promise.reject(reason)方法返回一个状态为Rejected的新Promise实例。

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

下面是promise的基本用法:

const promise = new Promise(function (resolve, reject) {
            // ... some code
            if (/* 异步操作成功 */) {
                resolve(value);
            } else {
                reject(error);
            }
        }).then(function (value) {
            // success
        }, function (error) {
            // failure
        });

简单案例1:

 function timeout(ms) {
        return new Promise((resolve, reject) => {
            setTimeout(resolve, ms, 'done');
            // resolve('done')
        });
    }

    timeout(100).then((value) => {
        console.log(value); //done
    });

上面代码中,timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(ms参数)以后,Promise实例的状态变为resolved,就会触发then方法绑定的回调函数。

简单案例2:

let promise = new Promise(function(resolve, reject) {
  console.log('Promise');
  resolve();
});

promise.then(function() {
  console.log('resolved.');
});

console.log('Hi!');

// Promise
// Hi!
// resolved

上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数, 将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。

简单案例3:

function loadImageAsync(url) {
            return new Promise(function (resolve, reject) {
                const image = new Image();

                image.onload = function () {
                    resolve(image);
                };

                image.onerror = function () {
                    reject(new Error('Could not load image at ' + url));
                };

                image.src = url;
            });
        }

上面代码中,使用Promise包装了一个图片加载的异步操作。如果加载成功,就调用resolve方法,否则就调用reject方法。

简单案例4.

下面是一个用Promise对象实现的 Ajax 操作的例子。

const getJSON = function (url) {
            const promise = new Promise(function (resolve, reject) {
                const handler = function () {
                    if (this.readyState !== 4) {
                        return;
                    }
                    if (this.status === 200) {
                        resolve(this.response);
                    } else {
                        reject(new Error(this.statusText));
                    }
                };
                const client = new XMLHttpRequest();
                client.open("GET", url);
                client.onreadystatechange = handler;
                client.responseType = "json";
                client.setRequestHeader("Accept", "application/json");
                client.send();

            });

            return promise;
        };

        getJSON("/posts.json").then(function (json) {
            console.log('Contents: ' + json);
        }, function (error) {
            console.error('出错了', error);
        });

上面代码中,getJSON是对 XMLHttpRequest 对象的封装,用于发出一个针对 JSON 数据的 HTTP 请求,并且返回一个Promise对象。需要注意的是,在getJSON内部,resolve函数和reject函数调用时,都带有参数。


Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

优点: 解决回调地狱
缺点: 无法取消promise ,错误需要通过回调函数获取 ,书写麻烦,不能实现异步代码,同步执行的需求(配合async函数使用即可)

可详细学习: https://es6.ruanyifeng.com/#docs/promise

3、 generator

    特点:可以控制函数的执行,可以配合 co 函数库使用

4、 async/await

async/await 是异步的终极解决方案。
要注意的是,await只能在函数中用async关键字标记后才能使用
1、async 表示这是一个async函数,await只能用在这个函数里面。
2、await 表示在这里等待promise返回结果了,再继续执行。
3、await 后面跟着的应该是一个promise对象(当然,其他返回值也没关系,只是会立即执行,不过那样就没有意义了…)

            Async函数表示函数里面可能会有异步方法,并且函数返回的是promise对象。遇到await会立即执行await表达式,然后再将其后的代码放入到微任务中,让出执行站让同步代码先执行。

下面再带领大家看一些面试中常出现的异步代码题:

  1. promise
 	console.log(0)
    new Promise(function(res,rej){
        console.log(1);
        // res()
        // rej()
    }).then(function(){
        console.log('then');
    }).catch(function(){
        console.log('catch');
    })
    console.log(3);

2.async/await

async function show(){
        console.log(1);
        await dev() // await 后面的代码为异步微任务代码
        console.log(2);
    }
    function dev(){
        console.log('await');
    }
    console.log('first');
    show()

3.综合练习

 async function async1(){
        console.log('async1 start');
        await async2();
        console.log('async1 end');
    }

    async function async2(){
        console.log('async2');
    }

    console.log('script start');

    setTimeout(function(){
        console.log('settimeout');
    },0)

    async1();

    new Promise(function(resolve){
        console.log('promise1');
        resolve()
    }).then(function(){
        console.log('promise2');
    })

    console.log('script end');

    //打印:script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2 -> settimeout

文章最后,感谢大家的观看,祝大家学有所成!

你可能感兴趣的:(异步处理)