js中如何顺序执行异步任务

这篇具有很好参考价值的文章主要介绍了js中如何顺序执行异步任务。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在js中,任务可分为两种,同步任务和异步任务。

(1) 同步任务

又叫 非耗时任务,指的是在主线程排队执行的那些任务
只有前一个任务执行完毕,才能执行后一个任务

(2) 异步任务

又叫 耗时任务,异步任务由JavaScript委托给宿主环境进行执行
当异步任务执行完成后,会通知JavaScript主线程执行异步任务的回调函数
当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务

现有a、b和c三个任务,如果其为同步任务,可以很简单地顺序执行,但如果其为异步任务,该如何顺序执行呢?

一、回调函数

function thing(thingName, callback) {
   
    setTimeout(() => {
   
        console.log(`执行${
     thingName}任务`)
        typeof callback === 'function' && callback()
    }, 1000)
}

// 执行a任务
// 执行b任务
// 执行c任务
thing('a', () => {
   
    thing('b', () => {
   
        thing('c')
    })
})

优点:简单、方便、实用

缺点:回调函数层层嵌套,不易于阅读和维护,形成回调地狱。

二、promise

1. 使用方式

基本使用

new Promise ((resolve, reject) => {
   
  // 执行代码
}).then(() => {
   
  // 期约兑现
}).catch(() => {
   
  // 期约拒绝
})

详细使用戳这里

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise#%E7%A4%BA%E4%BE%8B

2. 异步任务顺序执行

function thing(thingName) {
   
    return new Promise(resolve => {
   
        setTimeout(() => {
   
            console.log(`执行${
     thingName}任务`)
            resolve()
        }, 1000)
    })
}

// 执行a任务
// 执行b任务
// 执行c任务
thing('a')
    .then(() => thing('b'))
    .then(() => thing('c'))

3. 实现原理

那么如何实现一个promise呢?实现promise之前,先分析下promise的结构

  1. promise存在三个状态,pending 待定, fulfilled 兑现, rejected 拒绝,因此需要

    (1)定义三个常量 PENDINGFULFILLEDREJECTED 对应三种状态

    (2)定义 status 表示期约当前状态

    (3)定义 value 表示已兑现期约值、定义 reason 表示已拒绝期约原因

  2. 在调用promise的实例函数时,我们传入了一个执行器,执行器接收两个函数,其作用分别为将待定期约转化为已兑现期约与已拒绝期约。因此需要定义两个函数 resolve reject

  3. 已兑现期约、已拒绝期约处理函数 then

    已拒绝期约处理函数 catch

    最终执行函数 finally

  4. 静态函数 resolverejectall race 的实现

代码实现基于以下链接调整

https://juejin.cn/post/6945319439772434469#heading-30

3.1 promise简易实现
// MyPromise.js

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
   
  // 期约状态, 初始值为pending
  status = PENDING
  // 已兑现期约值
  value = null
  // 已拒绝期约原因
  reason = null

  constructor(executor){
   
    executor(this.resolve, this.reject)
  }

  // 将待定期约转化为已兑现期约
  resolve = (value) => {
   
    if (this.status === PENDING) {
   
      this.status = FULFILLED
      this.value = value
    }
  }

  // 将待定期约转化为已拒绝期约
  reject = (reason) => {
   
    if (this.status === PENDING) {
   
      this.status = REJECTED
      this.reason = reason
    }
  }

  then (onFulfilled, onRejected) {
   
    if (this.status === FULFILLED) {
   
      onFulfilled(this.value)
    } else if (this.status === REJECTED) {
   
      onRejected(this.reason)
    }
  }
}

module.exports = MyPromise

同目录下新建 test.js 文件用于测试

// test.js

// 引入我们的 MyPromise.js
const MyPromise = require('./MyPromise')
const promise1 = new MyPromise((resolve, reject) => {
   
   resolve('resolve')
})

const promise2 = new MyPromise((resolve, reject) => {
   
    reject('reject')
})

promise1.then(value => {
   
  console.log('promise1 then', value)
}, reason => {
   
  console.log('promise1 catch', reason)
})

promise2.then(value => {
   
    console.log('promise2 then', value)
  }, reason => {
   
    console.log('promise2 catch', reason)
  })

// 执行结果
// promise1 then resolve
// promise2 catch reject
3.2 加入异步逻辑

继续测试,发现异步执行resolve函数时,会存在问题。因此,我们需对异步逻辑进行处理。

// test.js

const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
   
   setTimeout(() => resolve('resolve'), 0)
})

promise.then(value => {
   
    console.log('promise then', value)
  }, reason => {
   
    console.log('promise catch', reason)
  })

// 期望输出 promise then resolve
// 实际无输出

(1) 缓存兑现与拒绝回调

// 存储兑现回调函数
onFulfilledCallback = null
// 存储拒绝回调函数
onRejectedCallback = null

(2) then 方法中新增待定期约处理

then (onFulfilled, onRejected) {
   
  if (this.status === FULFILLED) {
   
    onFulfilled(this.value)
  } else if (this.status === REJECTED) {
   
    onRejected(this.reason)
  }  else if (this.status === PENDING) {
   
    this.onFulfilledCallback = onFulfilled
    this.onRejectedCallback = onRejected
  }
}

(3) resolve 与 reject 中调用回调函数

  // 将待定期约转化为已兑现期约
  resolve = (value) => {
   
    if (this.status === PENDING) {
   
      this.status = FULFILLED
      this.value = value
      // 兑现回调函数存在则执行
      this.onFulfilledCallback && this.onFulfilledCallback(value)
    }
  }

  // 将待定期约转化为已拒绝期约
  reject = (reason) => {
   
    if (this.status === PENDING) {
   
      this.status = REJECTED
      this.reason = reason
      // 拒绝回调函数存在则执行
      this.onRejectedCallback && this.onRejectedCallback(reason)
    }
  }

使用以下代码再次验证,异步问题解决。

// test.js

const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
   
   setTimeout(() => resolve('resolve'), 0)
})

promise.then(value => {
   
    console.log('promise then', value)
  }, reason => {
   
    console.log('promise catch', reason)
  })

// 执行结果: promise then resolve
3.3 实现 then 方法多次调用添加多个处理函数

Promise支持添加多个处理函数,来测试下自定义Promise是否满足。

// test.js

const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
   
  setTimeout(() => {
   
    resolve('success')
  }, 2000) 
})

promise.then(value => {
   
  console.log(1)
  console.log('resolve', value)
})
 
promise.then(value => {
   
  console.log(2)
  console.log('resolve', value)
})

promise.then(value => {
   
  console.log(3)
  console.log('resolve', value)
})

// 3
// resolve success

经测试,自定义Promise并不能添加多个处理函数,继续修改。

(1) 新增兑现与拒绝回调数组

// 存储兑现回调函数数组
onFulfilledCallbacks = []
// 存储拒绝回调函数数组
onRejectedCallbacks = []

(2) then方法中存储回调

then (onFulfilled, onRejected) {
   
  if (this.status === FULFILLED) {
   
    onFulfilled(this.value)
  } else if (this.status === REJECTED) {
   
    onRejected(this.reason)
  }  else if (this.status === PENDING) {
   
    this.onFulfilledCallbacks.push(onFulfilled)
    this.onRejectedCallbacks.push(onRejected)
  }
}

(3) resolve 与 reject 中循环调用回调函数文章来源地址https://www.toymoban.com/news/detail-489142.html

// 将待定期约转化为已兑现期约
resolve = (value) => {
   
  if (this.status === PENDING) {
   
    this.status = FULFILLED
    this.value = value
    // 兑现回调函数存在则执行
    while (this.onFulfilledCallbacks.length) {
   

到了这里,关于js中如何顺序执行异步任务的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 测试平台——异步执行任务

    2023年08月21日
    浏览(48)
  • 宏任务与微任务执行顺序及对比记录

    目录 前言 一、 宏任务、微任务的基本概念 1.宏任务介绍 2.微任务介绍 3.宏任务、微任务发展及出现的原因:  4.宏任务、微任务的基本类型 二、 事件循环模型(Event Loop) 三、 Promise、async和await 在事件循环中的处理 1.Promise:         2. async/await : 这个知识点比较容易忽

    2024年01月21日
    浏览(47)
  • 【案例讲解】LVGL 向UI任务发送顺序执行的延时任务方法

    更多源码分析请访问: LVGL 源码分析大全 由于 lv_timer_create 的执行实现方式为 后进先出 的栈执行方式,这导致在添加一些需要顺序执行的延时任务时会很麻烦,因此,需要实现一个 先进先出 接口。 以ESP32中 examples/factory_demo 为例:ESP32上的LVGL是采用多任务(相当于Linux上的

    2024年01月16日
    浏览(40)
  • 使用多线程执行任务,并获取返回结果,附异步实现

    这里创建了一个包含三个线程的固定线程池 线程池的介绍 根据主机情况实现自定义线程池: 也可以通过继承 ThreadPoolExecutor 类来实现一个自定义线程池工具类。ThreadPoolExecutor 是 Java 标准库中提供的一个线程池实现,通过继承它,我们可以实现自定义的线程池。 下面是一个继

    2024年02月16日
    浏览(40)
  • Flutter实现ControlExecutor进行多个异步任务执行时监听状态并可指定最后执行的异步并在指定的异步执行完毕后结束executor并回调。

    1.场景 当有多个接口请求时,且接口调用不是同时进行时,而且接口调用有可能时链式的,中间也有可能加入别的逻辑,但是需要在第一个接口调用时打开等待框,在最后一个接口调用完成时关闭等待框类似需求时,可以用到ControlExecutor进行接口执行过程的监听,并可标记最

    2024年02月09日
    浏览(38)
  • 20230515学习笔记——js中的同步任务与异步任务,宏任务与微任务

    2023-05-15 (1)js中的同步任务与异步任务 ①同步任务是指:不耗时的任务,就是执行很快, ②异步任务是指:耗时的任务,它里面有一个机制是EventLoop(事件循环),即值耗时任务会被js分配到宿主环境中进行执行,执行后的结果放到一个“消息队列”中,当js将同步任务执行完

    2024年02月04日
    浏览(42)
  • JS执行机制--同步与异步

    单线程 JavaScript语言具有单线程的特点,同一个时间只能做一件事情。这是因为JavaScript脚本语言是为了处理页面中用户的交互,以及操作DOM而诞生的。如果对某个DOM元素进行添加和删除,不同同时进行。应该是先添加,再删除,事件有序。 单线程的特点是所有任务都需要排队

    2023年04月20日
    浏览(34)
  • js执行顺序

    1.首先js是单线程的 所有的同步任务都是按顺序依次执行的,前面的执行完了之后才会执行后面的任务。遇到计算量大的代码就会阻塞在这里。下面的代码就是打印输出10000次,当循环走完了之后 才会走后面的代码,输出的时间是981ms 输出981ms 2.因为js是单线程的,所有的任务

    2023年04月08日
    浏览(38)
  • 一文读懂异步和同步——async/await在运行时和普通代码的执行顺序的问题

    一、执行顺序问题 JavaScript中的await语句是异步编程中用于等待Promise对象执行结果的,它通常与async函数一起使用。在使用await时,程序的执行将暂停,直到该后面的Promise对象完成执行并返回结果。 函数代码执行顺序通常情况下按照代码所在文件从上至下

    2024年02月03日
    浏览(43)
  • Django 如何使用 Celery 完成异步任务或定时任务

    以前版本的 Celery 需要一个单独的库(django-celery)才能与 Django 一起工作, 但从 Celery 3.1 开始,情况便不再如此,我们可以直接通过 Celery 库来完成在 Django 中的任务。 以 Docker 安装为例,安装一个密码为 mypassword 的 Redis 服务端 在 Django 项目中创建一个 celery.py 文件,并配置

    2023年04月25日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包