Skip to content
文章摘要

深入浅出Promise

异步编程(JS基础的三大概念之一)

基础概念:同步行为和异步行为是计算机科学的一个基本概念。 JavaScript是单线程的,在单线程事件循环(event loop)模型中, 同步操作与异步操作是代码核心机制之一。

同步异步代码特点:

  1. 异步不会阻塞代码的执行
  2. 同步会阻塞代码的执行

代码演示

javascript
// 同步代码
console.log(1)
alert(2)
console.log(3)
// 异步代码

console.log(1)
setTimeout(console.log, 0, 2)
console.log(3)

常见异步的应用场景

  1. setTimeout、setInterval
  2. 网络请求(ajax)

Promise 是ES6里面新增异步编程的方案,Promise 生来就是为了解决异步操作的问题。在之前都是使用的回调函数来处理异步的, 比如Jquery中的$.get(),如果一个网络请求依赖前一个网络请求,就会写成如下:

javascript
$.get('/any.do', (res)=> {
    
    $.get(`/any1.do${res.id}`, (res2) => {
        
        $.get(`/any2.do`, (res3)=> {
            console.log(res3)
        })
    })
})

很显然,随着代码越来越复杂,回调策略是不具备扩展性的。“回调地狱”这个称呼就是名至实归了,代码维护起来就是噩梦。 使用Promise改写后的样子

javascript
function getData(url){
        return new Promise((resolve, reject)=> {
            $.get(url, (res) => {
                resolve(res)
            },(err)=> {
                reject(err)
            })
        })
    }
    
    getData('/any.do').then((res)=> {
        return getData(`/any1.do${res.id}`)
    }).then((res)=> {
        return getData(`/any2.do`)
    }).then((res)=> {
        console.log(res)
    }).catch(err => {
        console.log(err)
    })

这样子就是一个串联的程序,只有一层,虽然也是回调的模式,解决了深层次的嵌套的问题。

2010年Commonjs项目的实现的Promise/A规范日益流行起来,这个规范最终成为ES6的实现范本。 PromiseA+规范链接地址: https://promisesaplus.com/ 其他二大核心基础概念(原型原型链、作用域闭包)

Promise基础

  1. promise状态

promise有三种状态: pending、resolved(fulfilled)、rejected

pending:待定状态,执行了 executor 后,处于该状态

fulfilled:兑现状态,调用resolve()后,Promise 的状态更改为 fulfilled,且无法再次更改

rejected:拒绝状态,调用reject()后,Promise 的状态更改为 rejected,且无法再次更改

javascript
// 报错,必须要实现 executor方法
// new Promise()
let p1 = new Promise(()=>{})
let p2 = new Promise((resolve)=>{resolve()})
let p3 = new Promise((resolve,reject)=>{reject()})
let p4 = new Promise((resolve,reject)=>{
    resolve()
    reject()
})
console.log(p1)
console.log(p2)
console.log(p3)
console.log(p4)
  1. then方法 Promise.prototype.then方法: (onResolved, onRejected) => {}

    onResolved函数: 成功的回调函数 (value) => {}

    onRejected函数: 失败的回调函数 (reason) => {}

    说明: 指定用于得到成功value的成功回调和用于得到失败reason的失败回调,返回一个新的promise对象

  2. catch方法 Promise.prototype.catch方法: (onRejected) => {}

    onRejected函数: 失败的回调函数 (reason) => {}

    说明: then()的语法糖, 相当于: then(undefined, onRejected)

  3. Promise.resolve方法 Promise.resolve方法: (value) => {} value: 成功的数据或promise对象

    说明: 返回一个成功/失败的promise对象

  4. Promise.reject方法 Promise.reject方法: (reason) => {} reason: 失败的原因

    说明: 返回一个失败的promise对象

  5. Promise.all方法 Promise.all方法: (promises) => {} promises: 包含n个promise的数组

    说明: 返回一个新的promise, 只有所有的promise都成功才成功, 只要有一个失败了就直接失败

  6. Promise.race方法 Promise.race方法: (promises) => {} promises: 包含n个promise的数组

    说明: 返回一个新的promise, 第一个完成的promise的结果状态就是最终的结果状态

消灭异步回调async/await

  1. async 标记的函数是一个promise对象
javascript
async function fn(){
        return true
    }
    const f = fn()
    console.log("::::",f)
    f.then((res) => {
        console.log(res)
    })
  1. await相当于 Promise then
javascript
(async function () {
        let p = Promise.resolve(true)
        const value = await p // 相当于 Promise then
        const value1 = await true // 如果是非promse对象,会包装成promise对象 Promise.resolve(true)
        console.log(p)
        console.log(value)
        console.log(value1)
    })()

最后使用async改写后的样子

javascript
function getData(url){
        return new Promise((resolve, reject)=> {
            $.get(url, (res) => {
                resolve(res)
            },(err)=> {
                reject(err)
            })
        })
    }
    // 最后catch的地方可以统一在某一处维护处理,代码看起来更加简洁明了
    const res = await getData('/any.do')
    await getData(`/any1.do${res.id}`)
    await getData(`/any2.do`)

再谈Event Loop并且深度探究Promise执行过程

tips: Promise是属于微任务,今天代码就只演示Promise微任务的这种

执行顺序是同步代码(同步代码里面的微任务会推到回调队列里面)-> event loop重新检测回调队列是否有事件 -> 事件队列(清空到推送执行栈)

事件循环图