JavaScript Promise入门与进阶

认识 Promise

Promise 是在 ES6 中出现的语法,主要用于处理 JavaScript 中的异步执行流程,使其能够像同步代码一样处理。在深入了解 Promise 之前,我们先来了解一下什么是异步。

异步

异步处理方式会在不等待每一行代码执行完成的状态下继续执行下一行代码。例如:

javascript 复制代码
let str = 'init';
const func = () => {
    str = 'changed';
};
setTimeout(func, 3000);
console.log(str); // 输出 'init'

上述代码段的执行顺序如下:

  1. 声明字符串 str 并赋值为 init
  2. 声明方法 func,作用是将字符串 str 的值改为 changed
  3. 使用 setTimeout 在三秒后执行 func 更改 str 的值。
  4. 最后在控制台中输出字符串 str 的值。

关键在于第 3 步和第 4 步。在输出 console.log 时,程序并不会等待 setTimeout 执行完成,而是继续向下执行,直到三秒后才会回头执行 func 更改 str 的值。

虽然使用异步可以让执行速度不会因为某一行代码而卡住整个程序,但有时候我们仍然需要先通过 AJAX 获取数据,再继续执行处理数据的代码。

到这里是不是有点熟悉?在使用 JQueryajax 送出请求时就拥有了 async 这个属性来处理这个状况,当设置为 false 代表同步,这时就得等 ajax 等到响应完成后,才会继续执行下方的代码,就像下方的例子:

javascript 复制代码
let data = {}

//使用 async 将 ajax 请求改为同步。
$.ajax({
    url: '',
    async: false,
    success: (status)=>{
        data = status
    }
)}

//data为statue
console.log(data)

但因现今前端工程在网页中的份量越来越重的关系,需要处理同步处理的地方也越来越多, Promise 就这样诞生了。

Promise 的基本用法

如上所述,Promise 是用来处理异步操作的部分。在说明用法前,我们先通过图片简单了解一下原理:

上图为 Promise 的生命周期,每个 Promise 都会经过 pending 状态,在 pending 后分别会有成功时走向的的 fulfill ,及失败时的 reject ,并通过 .then() 在成功时接着处理数据,或是用 .catch() 对失败的情况进行处理,当然!不论是哪一条路,也可以再 return 一个新的 Promise 延续处理。

现在,我们来实现一个基本的 Promise:

javascript 复制代码
const newPromise = new Promise((resolve, reject)=>{
    /**成功时返回**/
    resolve(status)

    /**失败时返回**/
    reject(status)
}).then((data)=>{
    /**以 then 接续成功时的处理**/
}).catch((error)=>{
    /**以 catch 接续失败时的处理**/
})

一个基本的 Promise 会带有两个函数作为参数,这两个函数代表了生命周期内的 fulfillreject ,不论是成功时返回的 resolve 和失败时返回的 reject 都能传入参数, 使对应的 .then.catch 可以接收 Promise 内得到的数据,做出不同的处理,再稍微了解用法后,便可把第一个例子改为 Promise 版本:

javascript 复制代码
const newPromise = new Promise((resolve, reject)=>{
    setTimeout(()=>{resolve('changed')}, 3000)
}).then((data)=>{
    console.log(data) //3 秒后执行,并打印出'changed'
})

不同于第一个例子,通过 Promise 来处理获取数据,并把得到的数据通过 resolve 传到 .then 中处理,而 .then 内的代码也不会偷跑,会确实等到 Promise 内调用 resolve 结束后才会接着执行。

延续上方的例子,把 reject 对应的 catch 也加进去:

javascript 复制代码
const newPromise = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        if(Math.random() > 0.5){
            resolve('changed')
        }
        else{
            reject('error')
        }
    }, 3000)
}).then((data)=>{
    console.log(data)
}).catch((error)=>{
    console.log(error)
})

上方的在 setTimeout 内加上了 Math.random() 取随机数(会返回 0~1 之间的值),并借由判断该随机数是否大于 0.5 来模拟两种情况,当大于时代表成功,小于时代表失败,当然! .catch 也和 .then 相同,在没有 Promise 内的 reject 调用下是不会执行的。

Promise 进阶用法

了解过基本用法,我们知道 .then 能够在 Promise 中以 resolve 做同步的执行调用,但 .then 本身也可以拥有返回值,并由下一个 .then 接收继续执行:

javascript 复制代码
const newPromise = new Promise((resolve, reject)=>{
    setTimeout(()=>{resolve('changed')}, 3000)
}).then((data)=>{
    console.log(data)  // 'changed'
    return 'last changed'
}).then((data)=>{
    console.log(data)  // 'last changed'
})

第二个 .then 会接收上一个 .then 的返回值,并将它打印出,但是需要注意的是,这种多个 .then 的用法,并不适用于同步执行,也就是说只要脱离了 Promise 就会回到异步,例如以下例子:

javascript 复制代码
const newPromise = new Promise((resolve, reject)=>{
    setTimeout(()=>{resolve('changed')}, 3000)
}).then((data)=>{
    console.log(data)
    setTimeout(()=>{data = 'last changed'},2000)
    return data
}).then((data)=>{
    console.log(data) // 'changed'
})

上方例子在第一个 .then 中使用 setTimeout 在 2 秒后改变 data 的值并将他返回,但执行后会发现,在第二个 .then 中出现在 console 上的还是旧值,并不会等到 setTimeout 执行完后才接着下一个 .then

另外返回值也可以是另一个 Promise

javascript 复制代码
const lastPromise = new Promise((resolve, reject)=>{
    setTimeout(()=>{resolve('last changed')},2000)
})
const newPromise = new Promise((resolve, reject)=>{
    setTimeout(()=>{resolve('changed')}, 3000)
}).then((data)=>{
    console.log(data)
    return lastPromise.then((lastData)=>{return lastData})
}).then((data)=>{
    console.log(data) // 'last changed'
})

在第一个 .then 的返回了 lastPromise.then 返回的内容,也就是 last changed ,因此最后一个 .then 接收到的 data 也会是 last changed

上方例子也有需要注意的地方,那就是每个 Promise 都会有自己的生命周期,所以在 lastPromise 被建构出来的时候,就会开始进行 pending ,不会等到 newPromise 的第一个 .then 要求返回数据时才开始执行 setTimeout 的 2 秒,因此上方程序的总执行时间并不会是两个 setTimeout 加起来的时间 5 秒,而是 3 秒,因为在 Promise 外,都是异步的,所以两个 Promise 在建构后的 pending 是异步的。

如果需要将两个 Promise 的执行也变成同步,那只需将上方的 lastPromise 置于 newPromise.then 中就行了,如此一来就会变成 3 秒加上 2 秒的同步执行了:

javascript 复制代码
const newPromise = new Promise((resolve, reject)=>{
    setTimeout(()=>{resolve('changed')}, 3000)
}).then((data)=>{
    console.log(data)
    //第二个Promise
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{resolve('last ' + data)},2000)
    }).then((lastData)=>{
        return lastData
    })
}).then((data)=>{
    console.log(data) // 'last changed'
})

如此一来只有在进入 newPromise.then 时,才会去新建构另一个 Promise 并利用他的 .then 返回了一个新值,等到新建构的 Promise 生命周期结束后,才会执行 newPromise 最后一个 .then ,不过这种方式会累加执行的时间,即总共需要 3 秒 + 2 秒 = 5 秒。

以上就是本篇对 Promise 的介绍,希望对你有所帮助。如果文章中有任何问题或是不理解的地方,欢迎留言交流!

相关推荐
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang2 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
阮少年、5 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
郝晨妤6 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
AvatarGiser6 小时前
《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
前端·vue.js·elementui
喝旺仔la6 小时前
vue的样式知识点
前端·javascript·vue.js
别忘了微笑_cuicui6 小时前
elementUI中2个日期组件实现开始时间、结束时间(禁用日期面板、控制开始时间不能超过结束时间的时分秒)实现方案
前端·javascript·elementui