从JavaScript 异步函数、promise到async/await

1.JavaScript 是单线程语言

JavaScript是单线程的语言,因此所有的代码都是从上至下一步一步地执行,例如下面计算0~99999999的整数和的代码:

var t1 = Date.now()   //记录开始时间
var sum = 0
for (var i = 0; i < 100000000; i ++){
    sum += i
}
var t2 = Date.now()-t1  //记录结束时间
console.log("sum = " + sum + ",计算所需事件为:" + t2 + "ms"  )
// sum = 4999999950000000,计算所需时间为:968ms

上述代码在浏览器中运行完成所需时间968ms,而在这些代码执行完之前,浏览器无法执行后续的代码直至其完成。如果在浏览器中仅仅执行一次这样的代码并没有什么问题,但是如果是10次甚至是100次呢?浏览器就会变得卡顿甚至是崩溃。

在实际情况中javascript可能会发送大量的网络请求,并且每个请求都会消耗不可预估的时间,按顺序执行必然导致长时间等待,用户体验的大幅下降。

为了解决javascript的上述问题,异步就应运而生了。

2.JavaScript 异步

具体而言,JavaScript 异步就是发送第一个请求时,不等待请求的结果返回即发送下个请求。等到所有请求发送完毕后,哪个请求的结果先返回则处理这个请求。例如下面的代码:

//如果需要尝试执行下面的代码,请使用chrome浏览器访问baidu主页后再在console中执行)
var ajax = $.ajax({
    url: 'https://www.baidu.com/',
    success: function () {
        console.log('第一次请求成功')
    }
})
// 第一次请求成功

上面代码中$.ajax()需要传入两个参数url和success。其中url是请求路由,success是一个函数。这个函数传递过去不会立即执行,而是等着请求成功之后才会执行。对于这种传递过去但不立即执行,等代结果返回之后再执行的函数,叫做回调函数(callback)。

再看一段更加能说明回调函数的 nodejs 代码。和上面代码基本一样,唯一区别就是:上面代码时网络请求,而下面代码时 IO 操作。

var fs = require('fs')
fs.readFile('data1.json', (err, data) => {
    console.log(data.toString())
})

3.回调地狱(callback hell)

让我们再回顾一下异步中的ajax代码。

var ajax = $.ajax({
    url: 'https://www.baidu.com/',
    success: function () {
        console.log('第一次请求成功')
    }
})
// 第一次请求成功

如果我们想在这个ajax请求成功后再进行一次同样的ajax请求,代码就会变成这样:

var ajax = $.ajax({
    url: 'https://www.baidu.com/',
    success: function () {
        console.log('第一次请求成功')
        $.ajax({
            url: 'https://www.baidu.com/',
            success: function (data) {
                console.log('第二次请求成功')
            }
        })
    }
})
// 第一次请求成功
// 第二次请求成功

如果之后还有第三次请求的话,代码就会变得像这样:

var ajax = $.ajax({
    url: 'https://www.baidu.com/',
    success: function () {
        console.log('第一次请求成功')
        $.ajax({
            url: 'https://www.baidu.com/',
            success: function (data) {
                console.log('第二次请求成功')
                $.ajax({
                    url: 'https://www.baidu.com/',
                    success: function () {
                        console.log('第三次请求成功')
                    }
                })
            }
        })
    }
})
// 第一次请求成功
// 第二次请求成功
// 第三次请求成功

仅仅是三次回调,代码已经变得难以阅读。设想一下,如果之后还有第四次,第五次甚至是第十次,第一百次回调,那样代码就变得复杂不堪,难以直视。像这样拥有多层嵌套的回调函数就会产生回调地狱的问题。

为了解决回调地狱的问题,javascript在 ES 2015 中原生支持了Promise。

4.Promise

我们先用Promise来封装一下上一节中的ajax请求。

// 定义一个 promise 对象
const promise = new Promise((resolve, reject) =>{
    // 将之前的ajax请求,放到这个Promise对象中
    var ajax = $.ajax({
        url: 'https://www.baidu.com/',
        success: function () {
            console.log('第一次请求成功')
            //请求成功执行resolve(),将Promise对象的状态从Pending变成resolved
            resolve()
        }
    })
})

上述代码仅仅是将之前的ajax请求程序,用new Promise((resolve,reject) => {…})包装起来,并在回调函数success中执行resolve(),将promise对象的状态从Pending变成Resolved,触发下一个异步函数的执行。接着上面的代码我们继续写下去:

promise.then(()=>{
    var ajax = $.ajax({
        url: 'https://www.baidu.com/',
        success: function () {
            console.log('第二次请求成功')
        }
    })
}).then(()=>{
    var ajax = $.ajax({
        url: 'https://www.baidu.com/',
        success: function () {
            console.log('第三次请求成功')
        }
    })
})
// 第一次请求成功
// 第二次请求成功
// 第三次请求成功

由于上一段代码中的success执行了resolve(),Promise对象的状态从Pending变成Resolved,触发Promise对象的then()方法,并且then()方法可以进行链式操作,请求次数也可以根据需要继续增加。

以上,我们就通过Promise实现了多个异步操作的顺序执行,大大增加了代码的逻辑性以及可读性。

未完待续。。。

Add a Comment

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Close Bitnami banner