ES6-Promise:代码执行顺序 / 从上到下的顺序依次执行 / 像一条纸带 / 像一条河流 / promise / async await / vue 项目为什么封装接口请求要用promise

文章详细介绍了JavaScript代码的执行顺序,特别是涉及异步操作如网络请求和定时器时的情况。Promise作为异步编程的解决方案,用于管理异步操作的状态(pending,fulfilled,rejected)。文章通过示例展示了Promise如何解决回调地狱问题,以及使用Promise.all()、Promise.race()等方法。此外,还提供了Promise在实际项目中的应用示例,如实现sleep函数和封装HTTP请求。

一、理解JavaScript代码执行顺序

JavaScript 代码的执行顺序是按照从上到下的顺序依次执行的。也就是说,代码从脚本的第一行开始执行,到最后一行结束。像一条纸带。像一条河流。

然而,JavaScript 中还存在一些异步操作,比如网络请求、定时器等。这些操作不会阻塞代码的执行,会在后台继续执行,等待特定的条件满足后再执行相应的回调函数。因此,在遇到这些异步操作时,JavaScript 会先执行后续的代码,而不是等待异步操作完成。

二、理解promise / then

承诺 与 兑现

Promise将异步执行的程序变成同步执行,所谓的在开发中解决回调嵌套的问题
Promise 是异步编程的一种解决方案
从语法上讲,promise是一个对象
从它可以获取异步操作的消息
从本意上讲,它是承诺,承诺它过一段时间会给你一个结果
创造promise实例后,它会立即执行
Promise  对象用于表示一个异步操作的最终完成 (或失败)及其结果值

三、promise三种状态

待定(pending):初始状态,既没有被兑现,也没有被拒绝。
已兑现(fulfilled):操作成功。
已拒绝(rejected):操作失败。
promise状态一旦改变,就不会再变。

一个 Promise 对象代表一个在这个 promise 被创建出来时不一定已知的值。它让您能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。 这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 Promise ,以便在未来某个时候把值交给使用者。

待定状态的 Promise 对象要么会通过一个值被兑现(fulfilled),要么会通过一个原因(错误)被拒绝(rejected)。当这些情况之一发生时,我们用 promise 的 then 方法排列起来的相关处理程序就会被调用。如果 promise 在一个相应的处理程序被绑定时就已经被兑现或被拒绝了,那么这个处理程序就会被调用,因此在完成异步操作和绑定处理方法之间不会存在竞争状态。

四、Promise原理

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

五、promise解决回调嵌套问题

5.1、正常执行

function one (){ return 'i am one'}
function two (){ return 'i am two'}
function three (){ return 'i am three'}

function run (){
    console.log(one())
    console.log(two())
    console.log(three())
}
run()
// i am one
// i am two
// i am three

5.2、出现问题

function one (){ return 'i am one'}
function two (){
    setTimeout(()=>{
        return 'i am two'
    }, 2000)
}
function three (){ return 'i am three'}
function run (){
    console.log(one())
    console.log(two())
    console.log(three())
}
run()
// i am one
// undefined
// i am three

5.3、promise解决问题 

function one (){ return 'i am one'}
function two (){
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{
            resolve('i am two')
        }, 2000)
    })
}
function three (){ return 'i am three'}
function run (){
    console.log(one())
    console.log(two())
    console.log(three())
}
run()
// i am one
// Promise {<pending>}
// i am three

5.4、配合 async await

function one (){ return 'i am one'}
function two (){
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{
            resolve('i am two')
        }, 2000)
    })
}
function three (){ return 'i am three'}
async function run (){
    console.log(one())
    console.log(await two())
    console.log(three())
}
run()
// i am one
// Promise {<pending>}
// i am two
// i am three

六、Promise方法

6.1、Promise.all()

const createPromise = (delay, flag = true)=>{
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{
            flag ? resolve(console.log(`成功${delay}`)) : reject(console.log(`失败${delay}`))
        },delay)
    })
}

//----失败-------
createPromise(500, false)
//失败500
//-----成功----------
createPromise(500)
成功500


Promise.all(createPromise(1000), createPromise(2000), createPromise(3000))
    .then((res)=>{
        console.log('成功', res)
    })
    .catch((err)=>{
        console.log('失败', err)
    })
// 成功1000
// 成功2000
// 成功3000

 

6.2、Promise.allSettled()

6.3、Promise.any()

6.4、Promise.prototype.catch()

6.5、Promise.prototype.finally()

6.6、Promise.race() 谁快谁先执行

const createPromise = (delay, flag = true)=>{
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{
            flag ? resolve(console.log(`成功${delay}`)) : reject(console.log(`失败${delay}`))
        },delay)
    })
}

Promise.race(createPromise(3000), createPromise(2000, false), createPromise(1000))
    .then((res)=>{
        console.log('成功', res)
    })
    .catch((err)=>{
        console.log('失败', err)
    })
// 成功1000
// 失败2000
// 成功3000

  

6.7、Promise.reject()

6.8、 Promise.resolve()

6.9、Promise.prototype.then()

七、promise项目中使用

7.1、实现sleep函数(方法一)

  function sleep(ticks) {
    return Promise.resolve().then(() => {
      return ticks && sleep(ticks - 1);
    });
  }

7.2、实现sleep函数 (方法二)

function sleep (seconds) {
  return new Promise((resolve) => {
     setTimeout(() => {
       resolve()
     }, seconds * 1000)
  })
}

7.3、实现同步请求

function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
};

7.4、nuxt3项目promise简单封装请求

nuxt3:我们开始吧-开发-配置-部署_snow@li的博客-CSDN博客

import { ElMessage } from 'element-plus'
 
const fetch = (url: string, options?: any): Promise<any>  => {
    const token = useCookie("token");
    const headers = { // headers信息
        'Authorization' : `Bearer ${token.value}`
    }
    const { public: { baseUrl } } = useRuntimeConfig()
    const reqUrl = baseUrl + url
    return new Promise((resolve, reject) => {
        useFetch(reqUrl, { ...options, headers }).then(({ data }: any) => {
            const value = data.value
            if (!data._rawValue) {
                // 这里处理错误回调
                reject(value)
            }else if(data._rawValue.code !== '0'){
                ElMessage({
                    message: data._rawValue.msg,
                    type: 'error',
                })
            } else {
                console.log('40data', data._rawValue)
                resolve(ref(data))
            }
        }).catch((err: any) => {
            reject(err)
        })
    })
}
 
export default new class Http {
 
    get(url: string, params?: any): Promise<any> {
        return fetch(url, { method: 'get', params })
    }
 
    post(url: string, params?: any): Promise<any>  {
        return fetch(url, { method: 'post', params })
    }
 
    put(url: string, body?: any): Promise<any>  {
        return fetch(url, { method: 'put', body })
    }
 
    delete(url: string, body?: any): Promise<any>  {
        return fetch(url, { method: 'delete', body })
    }
}

7.5、vue项目为什么封装接口请求要用promise 

vue项目为什么封装接口请求要用promise
序号原因解释
1异步编程的简化网络请求通常是异步的,Promise提供了一种简洁的方式来处理异步操作。相比于传统的回调函数,Promise使得代码更加直观和易于管理。
2链式调用和错误处理

Promise支持链式调用(.then(), .catch(), .finally()),这使得你可以按顺序执行多个异步操作,并在任何一步捕获错误。这种方式避免了回调地狱(callback hell),使得代码更加清晰。

错误处理更加集中和一致,通过.catch()可以在一个地方处理所有可能发生的错误。

3与async/await的兼容性Promise是async/await的基础。async函数返回一个Promise,await表达式等待一个Promise完成。这使得在Vue组件或Vuex action中使用async/await变得非常方便,进一步简化了异步代码的编写。
4代码可读性和可维护性使用Promise封装接口请求,可以创建一个清晰的接口层,将网络请求的逻辑与业务逻辑分离。这有助于代码的模块化和可维护性。
Promise的链式调用和async/await语法使得异步代码看起来更像是同步代码,提高了代码的可读性。
5更好的错误处理和重试机制

Promise的.catch()方法允许你集中处理错误,这意味着你可以在一个地方定义通用的错误处理逻辑,如显示错误消息、记录日志或重试请求。

通过封装Promise,你可以轻松实现请求的重试机制,而不需要在每个请求点重复相同的逻辑。

6统一接口封装接口请求为Promise对象后,无论内部是使用XMLHttpRequest、fetch还是其他HTTP库,对外提供的接口都是一致的,这有助于保持代码的一致性。
7取消请求虽然Promise本身不直接支持取消请求,但你可以结合其他技术(如AbortController与fetch API)来实现取消请求的功能。封装请求为Promise后,你可以在这个封装层内实现取消逻辑,使得取消请求变得更加容易管理。

7.6、Promise同步请求

let myPromise = new Promise(function(resolve, reject){
    //当异步代码执行成功时,我们才会调用resolve(...)
    //当异步代码失败时就会调用reject(...)
    //在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或其他内容
    setTimeout(function(){
        resolve("执行成功!")
    }, 250);
});

myPromise.then(function(successMessage){
    //successMessage的值是上面调用resolve(...)方法传入的值
    console.log("Yay! " + successMessage);
});

7.7、Promise链式调用

let c = new Promise((resolve, reject)=>{
    resolve(100)
})
.then((res)=>{
    return new Promise((resolve)=>{
        resolve(800 + res)
    })
    // 这里的结果作为下一个then的res返回供使用
})
.then((res)=>{
    console.log(res)
})
// 900

7.8、新增弹窗点确定按钮,在页面方法执行确定对应的操作

7.8.1、页面点新增

<template>
  <div>
    <el-button plain @click="dialogAdd({})">
      <span>添加</span>
    </el-button>
    <dialog-add ref="dialogAdd"></dialog-add>
  </div>
</template>
<script>

export default {
  components: {},
  data() {
    return {}
  },
  mounted() {},
  methods: {
    dialogAdd(){
      this.$refs.dialogAdd.show({}).then(({radioValue}) => {
        console.log('346', radioValue)
        this.$drawer(Details, { 
          title:  '新建成功',
          complete: () => {},
          attrs: {}
        })
      })
    }
  }
}
</script>
<style lang="less" scoped>
</style>

7.8.2、dialog页面

<template>
  <div class="dialog_box">
    <m-dialog
      title="哈哈"
      :visible.sync="visible"
      width="600px"
      height="515px"
      @closeBtn="closeBtn"
      @refuse="closeBtn"
      @confirm="confirm"
      :close-on-click-modal="false"
    >
      <template #content>
        ...
      </template>
    </m-dialog>
  </div>
</template>

<script>

export default {
  name: "AA",
  data() {
    return {
        radioValue:null,
        visible:false,
        resolve:null,
    };
  },
  methods:{
    async show({}){
      return new Promise((resolve) => {
          this.resolve = resolve
      })
    },
    // 点击确定--列表页面dialogAdd方法开始执行
    confirm(){
      this.resolve({radioValue:this.radioValue})
    },
  }
}
</script>
<style lang="less" scoped>
</style>

7.8.3、过程描述

业务页面点击新增按钮, 调用dialog的show方法,show方法内埋下一个promise,点击dialog的确定按钮执行confirm方法,confirm方法里边给到promise的resolve,这是业务页面.then方法内的代码执行。

八、相关内容

宏任务、微任务 、Generator 、Async / Await

九、AI问答:代码的执行 像一条纸带 / 像一条河流 这样说对吗

像一条纸带
序号特点解释
1连续性纸带通常被视为一种连续、线性的介质。将代码执行比作纸带,可能强调代码执行的顺序性和连续性。每一行代码就像纸带上的一个点或字符,依次被执行。
2静态性纸带本身是一个静态的对象,虽然可以表示连续的流程,但不如河流那样具有动态变化的特性。因此,这个比喻可能更适合用来描述代码的结构或流程,而不是其动态执行过程。
像一条河流
序号特点解释
1动态性河流是流动的,不断变化的。将代码执行比作河流,更能体现出代码执行过程中的动态性和变化性。例如,代码中的变量值可能会随着程序的运行而改变,就像河流中的水流一样不断变化。
2并发性河流中的水流可以分支、合并,形成复杂的网络。类似地,现代编程语言中的并发和并行执行也可以被视为代码执行的“分支”和“合并”,使得代码的执行路径更加复杂和多变。
这两种说法都有其合理之处,但侧重点不同。如果你想要强调代码执行的顺序性和连续性,那么“像一条纸带”可能更合适;而如果你想要强调代码执行的动态性和变化性(包括并发和并行执行),那么“像一条河流”可能更贴切。
这两种比喻都是对代码执行过程的一种形象描述,可以帮助人们更好地理解代码执行的特点。在实际使用中,可以根据语境和需要选择合适的比喻来增强表达效果。

十、欢迎交流指正

十一、参考链接

Promise - JavaScript | MDN

(15条消息) Promise不会??看这里!!!史上最通俗易懂的Promise!!!_weixin_33704234的博客-CSDN博客

(15条消息) promise的用法与定义。_一直在路上的小仙女的博客-CSDN博客_promise的用法

(15条消息) Promise到底解决了什么问题?_weixin_34335458的博客-CSDN博客

ES6 Promise用法讲解 - Samve - 博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值