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

JS中异步处理执行顺序 本文对JS执行做一个扩展。JavaScript是单线程语言,同一时间只能做一件事,只有当前一个任务结束,才会执行下一个任务。假如前一个任务执行时间较长,下一个任务还得等待前一个任务执行完成。在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)定义三个常量 PENDING、FULFILLED、REJECTED 对应三种状态

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

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

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

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

    已拒绝期约处理函数 catch

    最终执行函数 finally

  4. 静态函数 resolve、reject、 all 及 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 中循环调用回调函数

// 将待定期约转化为已兑现期约
resolve = (value) => {
  if (this.status === PENDING) {
    this.status = FULFILLED
    this.value = value
    // 兑现回调函数存在则执行
    while (this.onFulfilledCallbacks.length) {
      // Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
      this.onFulfilledCallbacks.shift()(value)
    }
  }
}

// 将待定期约转化为已拒绝期约
reject = (reason) => {
  if (this.status === PENDING) {
    this.status = REJECTED
    this.reason = reason
    // 拒绝回调函数存在则执行
    while (this.onRejectedCallbacks.length) {
      // Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
      this.onRejectedCallbacks.shift()(reason)
    }
  }
}

再次测试,问题解决。

// 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)
})

// 1
// resolve success
// 2
// resolve success
// 3
// resolve success
3.4 实现then方法的链式调用

promise是支持链式调用的,我们用自定义的promise来测试下,看是否满足。

// test.js

const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
  // 目前这里只处理同步的问题
  resolve('success')
})

function other () {
  return new MyPromise((resolve, reject) =>{
    resolve('other')
  })
}
promise.then(value => {
  console.log(1)
  console.log('resolve', value)
  return other()
}).then(value => {
  console.log(2)
  console.log('resolve', value)
  
// TypeError: Cannot read property 'then' of undefined

可以看到,第一个then函数的返回值为undefined,不能链式调用。继续修改

class MyPromise {
	......

  then (onFulfilled, onRejected) {
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        const x = onFulfilled(this.value)
        // 传入 resolvePromise 集中处理
        resolvePromise(x, resolve, reject)
      } else if (this.status === REJECTED) {
        onRejected(this.reason)
      }  else if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(onFulfilled)
        this.onRejectedCallbacks.push(onRejected)
      }
    })

    return promise2
  }
}

function resolvePromise(x, resolve, reject) {
  // 判断x是不是 MyPromise 实例对象
  if(x instanceof MyPromise) {
    // 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
    // x.then(value => resolve(value), reason => reject(reason))
    // 简化之后
    x.then(resolve, reject)
  } else{
    // 普通值
    resolve(x)
  }
}

测试,完成链式调用。

// test.js

const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
  // 目前这里只处理同步的问题
  resolve('success')
})

function other () {
  return new MyPromise((resolve, reject) =>{
    resolve('other')
  })
}
promise.then(value => {
  console.log(1)
  console.log('resolve', value)
  return other()
}).then(value => {
  console.log(2)
  console.log('resolve', value)
})

// 1
// resolve success
// 2
// resolve other
3.5 then 方法链式调用识别 Promise 是否返回自己

如果 then 方法返回的是自己的 Promise 对象,则会发生循环调用,这个时候程序会报错,原生Promise测试如下

// test.js

const promise = new Promise((resolve, reject) => {
    resolve(100)
  })

const p1 = promise.then(value => {
    console.log(value)
    return p1
})

// 100
// UnhandledPromiseRejectionWarning: TypeError: Chaining cycle detected for promise #<Promise>

在 MyPromise 实现一下

// MyPromise.js

class MyPromise {
  ......
  then (onFulfilled, onRejected) {
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        const x = onFulfilled(this.value)
        // 传入 resolvePromise 集中处理
        resolvePromise(promise2, x, resolve, reject)
      } else if (this.status === REJECTED) {
        onRejected(this.reason)
      }  else if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(onFulfilled)
        this.onRejectedCallbacks.push(onRejected)
      }
    })

    return promise2
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  // 如果相等了,说明return的是自己,抛出类型错误并返回
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }

  // 判断x是不是 MyPromise 实例对象
  if(x instanceof MyPromise) {
    // 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
    // x.then(value => resolve(value), reason => reject(reason))
    // 简化之后
    x.then(resolve, reject)
  } else{
    // 普通值
    resolve(x)
  }
}
// test.js

const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
    resolve('success')
})
 
const p1 = promise.then(value => {
   console.log('resolve', value)
   return p1
})
 

运行一下,结果报错了。从错误提示可以看出,我们必须要等 p1 完成初始化。这里就需要创建一个异步函数去等待 p1 完成初始化,此处使用微任务 --> queueMicrotask

image.png

修改并执行

// MyPromise.js

class MyPromise {
  ......

	then (onFulfilled, onRejected) {
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        queueMicrotask(() => {
          const x = onFulfilled(this.value)
          // 传入 resolvePromise 集中处理
          resolvePromise(promise2, x, resolve, reject)
        })
      } else if (this.status === REJECTED) {
        onRejected(this.reason)
      }  else if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(onFulfilled)
        this.onRejectedCallbacks.push(onRejected)
      }
    })

    return promise2
  }
}
// test.js

const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
    resolve('success')
})
 
const p1 = promise.then(value => {
   console.log('resolve1', value)
   return p1
})
 
// 运行的时候会走reject
p1.then(value => {
  console.log('resolve2', value)
}, reason => {
  console.log('reject')
  console.log(reason.message)
})

// 执行结果
// resolve1 success
// reject
// Chaining cycle detected for promise #<Promise>
3.6 错误处理

(1) 捕获执行器错误

// MyPromise.js

class MyPromise {
  ......

	constructor(executor){
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      // 如果有错误,就直接执行 reject
      this.reject(error)
    }
  }
}

测试一下

// test.js

const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
    // resolve('success')
    throw new Error('执行器错误')
})

// 2
// 执行器错误
promise.then(value => {
  console.log(1)
  console.log('resolve', value)
}, reason => {
  console.log(2)
  console.log(reason.message)
})

测试通过。

(2) then 执行的时错误捕获

// MyPromise.js

class MyPromise {
  ......

	then (onFulfilled, onRejected) {
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        try {
          queueMicrotask(() => {
            const x = onFulfilled(this.value)
            // 传入 resolvePromise 集中处理
            resolvePromise(promise2, x, resolve, reject)
          })
        }  catch (error) {
          reject(error)
        }  
      } else if (this.status === REJECTED) {
        onRejected(this.reason)
      }  else if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(onFulfilled)
        this.onRejectedCallbacks.push(onRejected)
      }
    })

    return promise2
  }
}

测试一下

// test.js

const MyPromise = require('./MyPromise')

const promise = new MyPromise((resolve, reject) => {
    resolve('success')
 })
 
//  1
//  resolve success
//  4
//  then error
promise.then(value => {
  console.log(1)
  console.log('resolve', value)
  throw new Error('then error')
}, reason => {
  console.log(2)
  console.log(reason.message)
}).then(value => {
  console.log(3)
  console.log(value)
}, reason => {
  console.log(4)
  console.log(reason.message)
})

测试通过。

3.7 rejected及pending状态改造
  1. 增加异步状态下的链式调用
  2. 增加回调函数执行结果的判断
  3. 增加识别 Promise 是否返回自己
  4. 增加错误捕获
// MyPromise.js

class MyPromise {
  ......

then (onFulfilled, onRejected) {
    const promise2 = new MyPromise((resolve, reject) => {
      const fulfilledMicrotask = () =>  {
        // 创建一个微任务等待 promise2 完成初始化
        queueMicrotask(() => {
          try {
            // 获取成功回调函数的执行结果
            const x = onFulfilled(this.value)
            // 传入 resolvePromise 集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          } 
        })  
      }
  
      const rejectedMicrotask = () => { 
        // 创建一个微任务等待 promise2 完成初始化
        queueMicrotask(() => {
          try {
            // 调用失败回调,并且把原因返回
            const x = onRejected(this.reason)
            // 传入 resolvePromise 集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          } 
        }) 
      }

      if (this.status === FULFILLED) {
        fulfilledMicrotask()
      } else if (this.status === REJECTED) {
        rejectedMicrotask()
      }  else if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(fulfilledMicrotask)
        this.onRejectedCallbacks.push(rejectedMicrotask)
      }
    })

    return promise2
  }
}
3.8 then 中的参数变为可选

上面我们处理 then 方法的时候都是默认传入 onFulfilled、onRejected 两个回调函数,但是实际上原生 Promise 是可以选择参数的单传或者不传,都不会影响执行。

例如下面这种 👇

// test.js

const promise = new Promise((resolve, reject) => {
  resolve(100)
})

promise
  .then()
  .then()
  .then()
  .then(value => console.log(value))

// 输出 100

所以我们需要对 then 方法做一点小小的调整

// MyPromise.js

class MyPromise {
  ......

	then (onFulfilled, onRejected) {
    // 如果不传,就使用默认函数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

    const promise2 = new MyPromise((resolve, reject) => {
      const fulfilledMicrotask = () =>  {
        // 创建一个微任务等待 promise2 完成初始化
        queueMicrotask(() => {
          try {
            // 获取成功回调函数的执行结果
            const x = onFulfilled(this.value)
            // 传入 resolvePromise 集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          } 
        })  
      }
  
      const rejectedMicrotask = () => { 
        // 创建一个微任务等待 promise2 完成初始化
        queueMicrotask(() => {
          try {
            // 调用失败回调,并且把原因返回
            const x = onRejected(this.reason)
            // 传入 resolvePromise 集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          } 
        }) 
      }

      if (this.status === FULFILLED) {
        fulfilledMicrotask()
      } else if (this.status === REJECTED) {
        rejectedMicrotask()
      }  else if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(fulfilledMicrotask)
        this.onRejectedCallbacks.push(rejectedMicrotask)
      }
    })

    return promise2
  }
}

改造完自然是需要验证一下的

先看情况一:resolve 之后

// test.js

const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
  resolve('succ')
})
 
promise.then().then().then(value => console.log(value))

// 打印 succ

再看情况二:reject 之后

// test.js

const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
  reject('err')
})
 
promise.then().then().then(value => console.log(value), reason => console.log(reason))

// 打印 err
3.9 catch、finally函数实现

(1) catch 其实是个语法糖,可以通过then函数传入第二个参数实现

(2) finally 的特点如下

无论promise成功或失败,finally方法都会执行接收到的回调函数,并返回一个promise实例:
a. 如果回调函数执行出错,将以抛出的错误,拒绝新的promise;
b. 否则,新返回的promise会沿用旧promise的决议值进行决议。

// MyPromise.js

class MyPromise {
  ......

  catch (onRejected) {
    return this.then(null, onRejected)
  }

  // 无论promise成功或失败,finally方法都会执行接收到的回调函数,并返回一个promise实例:
  // 1. 如果回调函数执行出错,将以抛出的错误,拒绝新的promise;
  // 2. 否则,新返回的promise会沿用旧promise的决议值进行决议。
  finally (callback) {
    return this.then(
      (data) => {
        callback()
        return data
      },
      (error) => {
        callback()
        throw error
      }
    )
  }
}
3.10 静态函数实现

(1) resolve

  static resolve (parameter) {
    // 如果传入 MyPromise 就直接返回
    if (parameter instanceof MyPromise) {
      return parameter
    }

    return new MyPromise(resolve =>  {
      resolve(parameter)
    })
  }

(2) reject

// MyPromise.js

class MyPromise {
  ......

  static reject (reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }
}

(3) all

// MyPromise.js

class MyPromise {
  ......

  static all (promiseArray) {
    let result = []
    let success = 0

    return new Promise((resolve, reject) => {
      promiseArray.forEach((promise, i) => {
        MyPromise.resolve(promise)
          .then(res => {
            result[i] = res
            success++

            // 全部成功
            if (success === promiseArray.length) {
              resolve(result)
            }
          }, err => reject(err))
      })
    }) 
  }
}

(4) race

// MyPromise.js

class MyPromise {
  ......

  static race (promiseArray) {
    return new Promise((resolve, reject) => {
      promiseArray.forEach((promise) => {
        MyPromise.resolve(promise)
          .then(res => {
              resolve(res)
          }, err => reject(err))
      })
    }) 
  }
}

验证一下,木有问题

// test.js

const MyPromise = require('./MyPromise')

// resolve success
MyPromise
  .resolve('success')
  .then((res) => {
    console.log('resolve', res)
  })

// reject fail
MyPromise
  .reject('fail')
  .then(() => {}, err => {
    console.log('reject', err)
  })

// all [ 'a', 'b', 'c' ]
MyPromise
  .all([thing('a', 1000), thing('b', 2000), thing('c', 3000)])
  .then(res => console.log('all', res))

// race a
MyPromise
  .race([thing('a', 1000), thing('b', 2000), thing('c', 3000)])
  .then(res => console.log('race', res))


function thing (thingName, timeout) {
  return new MyPromise(
    resolve => setTimeout(() => {
      resolve(thingName)
    }, timeout)
  )
}
3.11 最终代码
// MyPromise.js

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

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

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

  constructor(executor){
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      // 如果有错误,就直接执行 reject
      this.reject(error)
    }
  }

  // resolve 静态方法
  static resolve (parameter) {
    // 如果传入 MyPromise 就直接返回
    if (parameter instanceof MyPromise) {
      return parameter
    }

    return new MyPromise(resolve =>  {
      resolve(parameter)
    })
  }

  // reject 静态方法
  static reject (reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }

  static all (promiseArray) {
    let result = []
    let success = 0

    return new Promise((resolve, reject) => {
      promiseArray.forEach((promise, i) => {
        MyPromise.resolve(promise)
          .then(res => {
            result[i] = res
            success++

            // 全部成功
            if (success === promiseArray.length) {
              resolve(result)
            }
          }, err => reject(err))
      })
    }) 
  }

  static race (promiseArray) {
    return new Promise((resolve, reject) => {
      promiseArray.forEach((promise) => {
        MyPromise.resolve(promise)
          .then(res => {
              resolve(res)
          }, err => reject(err))
      })
    }) 
  }

  // 将待定期约转化为已兑现期约
  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
      // 兑现回调函数存在则执行
      while (this.onFulfilledCallbacks.length) {
        // Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
        this.onFulfilledCallbacks.shift()(value)
      }
    }
  }

  // 将待定期约转化为已拒绝期约
  reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason
      // 拒绝回调函数存在则执行
      while (this.onRejectedCallbacks.length) {
        // Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
        this.onRejectedCallbacks.shift()(reason)
      }
    }
  }

  then (onFulfilled, onRejected) {
    // 如果不传,就使用默认函数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

    const promise2 = new MyPromise((resolve, reject) => {
      const fulfilledMicrotask = () =>  {
        // 创建一个微任务等待 promise2 完成初始化
        queueMicrotask(() => {
          try {
            // 获取成功回调函数的执行结果
            const x = onFulfilled(this.value)
            // 传入 resolvePromise 集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          } 
        })  
      }
  
      const rejectedMicrotask = () => { 
        // 创建一个微任务等待 promise2 完成初始化
        queueMicrotask(() => {
          try {
            // 调用失败回调,并且把原因返回
            const x = onRejected(this.reason)
            // 传入 resolvePromise 集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          } 
        }) 
      }

      if (this.status === FULFILLED) {
        fulfilledMicrotask()
      } else if (this.status === REJECTED) {
        rejectedMicrotask()
      }  else if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(fulfilledMicrotask)
        this.onRejectedCallbacks.push(rejectedMicrotask)
      }
    })

    return promise2
  }

  catch (onRejected) {
    return this.then(undefined, onRejected)
  }

  finally (finalFunc) {
    return this.then(finalFunc, finalFunc)
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  // 如果相等了,说明return的是自己,抛出类型错误并返回
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }

  // 判断x是不是 MyPromise 实例对象
  if(x instanceof MyPromise) {
    // 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
    // x.then(value => resolve(value), reason => reject(reason))
    // 简化之后
    x.then(resolve, reject)
  } else{
    // 普通值
    resolve(x)
  }
}

module.exports = MyPromise

4. Promise A+ 测试

4.1 测试

检验一份手写 Promise 靠不靠谱,通过 Promise A+ 规范自然是基本要求,这里我们可以借助 promises-aplus-tests 来检测我们的代码是否符合规范。

(1) 新建test文件夹

(2) 命令行输入以下代码快速初始化

npm init -y

(3) 安装包

npm install promises-aplus-tests -D

(4) 将 MyPromise.js 拷贝至test文件夹,并添加 deferred 函数

// MyPromise.js

......

MyPromise.deferred = function () {
  const result = {}
  result.promise = new MyPromise(function (resolve, reject) {
    result.resolve = resolve
    result.reject = reject
  })

  return result
}

module.exports = MyPromise

(5) 配置启动命令

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "promises-aplus-tests MyPromise"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "promises-aplus-tests": "^2.1.2"
  }
}

(6) 命令行输入以下代码执行

node run test
4.2 修改

执行结果如图

image.png

image.png

可以看出是2.3.x的问题,翻看了一下 Promise A+ 规范,找到以下信息。

Promise A+ 规范链接:https://promisesaplus.com/#point-59image.png

翻译如下

2.3.3 否则如果 x 是一个对象或函数,

​ 2.3.3.1 将x.then赋值给then

​ 2.3.3.2 如果取 x.then 的值时抛出错误 e,则以 e为由拒绝 promise。

​ 2.3.3.3 如果then是一个函数,执行并将其this指向x,第一个入参为resolvePromise,第二个入参为rejectPromise

​ 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)

​ 2.3.3.3.2 如果 rejectPromise以r 为参数被调用,则以r为由拒绝promise

​ 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用

​ 2.3.3.3.4 如果then执行时抛出异常

​ 2.3.3.3.4.1 如果resolvePromise 或 rejectPromise 被调用,则忽略它

​ 2.3.3.3.4.2 否则,以e为由拒绝promise

​ 2.3.3.4 如果then不是一个函数,则以x为值兑现promise

2.3.4 如果x不是一个对象或函数,则以x为值兑现promise

代码修改如下

function resolvePromise(promise, x, resolve, reject) {
  // 如果相等了,说明return的是自己,抛出类型错误并返回
  if (promise === x) {
    return reject(new TypeError('The promise and the return value are the same'))
  }

  if (typeof x === 'object' || typeof x === 'function') {
    // x 为 null 直接返回,走后面的逻辑会报错
    if (x === null) {
      return resolve(x)
    }

    let then
    try {
      // 把 x.then 赋值给 then 
      then = x.then
    } catch (error) {
      // 如果取 x.then 的值时抛出错误 error ,则以 error 为据因拒绝 promise
      return reject(error)
    }

    // 如果 then 是函数
    if (typeof then === 'function') {
      let called = false
      try {
        then.call(
          x, // this 指向 x
          // 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
          y => {
            // 如果 resolvePromise 和 rejectPromise 均被调用,
            // 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
            // 实现这条需要前面加一个变量 called
            if (called) return
            called = true
            resolvePromise(promise, y, resolve, reject)
          },
          // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
          r => {
            if (called) return
            called = true
            reject(r)
          })
      } catch (error) {
        // 如果调用 then 方法抛出了异常 error:
        // 如果 resolvePromise 或 rejectPromise 已经被调用,直接返回
        if (called) return

        // 否则以 error 为据因拒绝 promise
        reject(error)
      }
    } else {
      // 如果 then 不是函数,以 x 为参数执行 promise
      resolve(x)
    }
  } else {
    // 如果 x 不为对象或者函数,以 x 为参数执行 promise
    resolve(x)
  }
}

再次执行,完美解决。

image.png

三、generator

generator函数,即生成器函数,是一个状态机,封装了多个内部状态。执行一个generator,会返回一个迭代器对象,通过迭代器对象,可以遍历generator函数内部的每个状态。因此,generator函数可以看做是一个迭代器生成器。

1. 基本使用

(1) generator基本形式

generator 函数是在 function 和函数名之间添加 * 来定义的。yield关键字可以让生成器停止和开始执行,生成器函数在遇到yield关键字之前会正常执行。遇到这个关键字之后,执行会停止,函数作用域的状态会被保留。停止执行的生成器函数可通过next()方法来恢复执行。

function *foo() {
    yield 1
    yield 2
    yield 3
    return 4
}

(2) 执行foo得到一个迭代器

const iterator = foo()

(3) 迭代器遍历

可以通过常用迭代器遍历方法如for-of来遍历迭代器

// 1
// 2
// 3
for(let item of iterator){
    console.log(item)
}

也可以手动进行遍历

// 1
// 2
// 3
let item = iterator.next()
while (!item.done) {
    console.log(item.value)
    item = iterator.next()
}

2. 异步任务顺序执行

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

function *generator() {
    yield thing('a')
    yield thing('b')
    yield thing('c')
}

function dothings (generator) {
    const thingIterator = generator()
    let thingItem
    exec()

    function exec () {
        thingItem = thingIterator.next()
        if (!thingItem.done) {
            Promise.resolve(thingItem.value).then(exec)
        }
    }
}

// 执行a任务
// 执行b任务
// 执行c任务
dothings(generator)

四、async/await

1. 实现原理

1)async/await 就是 generator 的语法糖,使得异步操作变得更加方便
2)async 函数就是将 generator 函数的星号(*)替换成 async,将 yield 替换成await

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

// generator 生成器函数
function myAsync (generator) {
    return function () {
        const iterator = generator()
        let item

        return new Promise ((resolve, reject) => exec(resolve, reject))

        function exec (resolve, reject) {
            item = iterator.next()

            if (!item.done) {
               Promise.resolve(item.value).then(() => exec(resolve, reject))
            } else {
                // 返回值处理
                if (item.value instanceof Promise) {
                    item.value.then(resolve, reject)
                } else {
                    resolve(item.value)
                }
            }
        }
    }
}

function *generator() {
    yield thing('a')
    yield thing('b')
    yield thing('c')

    return 'the end'
}

// 模拟定义一个async函数
const myAsyncFunc = myAsync(generator)
// 执行a任务
// 执行b任务
// 执行c任务
const result = myAsyncFunc()

// 返回为一个promise对象
console.log(result instanceof Promise) // true
// the end
result.then(res => console.log(res))

2. 使用注意事项

1)await 后面的promise对象,当其为一个已拒绝期约时,会阻塞后面代码的执行,举例如下

// the first
// UnhandledPromiseRejectionWarning: undefined
async function test () { 
    console.log('the first')
    await Promise.reject()
    console.log('the second')
}

test()

对此,可以使用catch函数或try-catch进行处理

catch函数

// the first
// the second
async function test () { 
    console.log('the first')
    await Promise.reject().catch(() => {})
    console.log('the second')
}

test()

try-catch

// the first
// the second
async function test () { 
    console.log('the first')
    try {
        await Promise.reject()
    } catch (e) {

    }
    console.log('the second')
}

test()

2)多个await后面的异步操作,如果不存在继发关系,最好让它们同时触发

3)await仅能在async函数中使用

4)async函数返回值为一个promise对象

3. 异步任务顺序执行

a、b、c异步任务顺序执行代码如下:

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

async function dothings () {
    await thing('a')
    await thing('b')
    await thing('c')
}

// 执行a任务
// 执行b任务
// 执行c任务
dothings()
Javascript异步执行不按顺序解决方案 主要介绍了Javascript异步执行不按顺序解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 立即下载

相关推荐

JS多个异步请求 按顺序执行next实现解析

主要介绍了js多个异步请求 按顺序执行next实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

JS 如何顺序执行多个异步任务?

在 JS 中可以使用 Promise 和 async/await 来顺序执行异步任务。生成器(Generator)确实是一种特殊的函数,其函数体内可以使用。使用 Promise 通过 then() 方法的链式调用来实现。使用 async/await 可以将异步任务看做同步人物来执行。生成器函数的调用返回一个迭代器对象,该迭代器对象可以通过调用。暂停函数的执行,将一个值传递给生成器的调用者。方法来依次获取生成器函数中使用。表示生成器函数是否已经执行完成。声明,并且在函数体内可以通过。

weixin_63490373的博客 1416

Windows Server 2022域控环境下的MDT部署工具安装与配置避坑指南

本文详细介绍了在Windows Server 2022域控环境中安装和配置MDT(Microsoft Deployment Toolkit)的全流程,包括ADK与Windows PE的精准安装、MDT高级配置、部署工作流构建及性能优化技巧。通过实战指南帮助IT管理员实现自动化系统部署,提升企业IT运维效率,特别适用于需要静默下发系统的大规模部署场景。

weixin_28481133的博客 439

Js 异步请求按顺序调用解决方案(真实工作场景,axios、Promise、async await)

需求背景:现在需要调用多个异步请求,要求某个请求调用成功之后,接着调另外的请求,有时候需要用上一个请求得到的结果,作为下一个请求的参数或者条件,继续调用另一个请求。 演示准备: 安装axios npm install axios --save 全局使用 //main.js import axios from 'axios' Vue.prototype.$axios = axios 实现一:Promise Promise 是一个 ECMAScript 6 提供的类,目的是更加优雅地书写复.

高大志leo的博客 9454

vivado实现SDI接口

XAPP1249,用来介绍UHD_SDI的实现。

Huskar_Liu的博客 7076

Qt自带绘图与GDI+绘图方式比较_gdi+实时绘制qt(2)

2、GDI+是Windows操作系统中的图形设备接口,它提供了一套用于绘制图形的API,包括了图形渲染、图形变换、图形剪切、图形填充等功能,使开发者可以快速地开发出各种类型的图形应用程序。1、Qt是一个跨平台的C++应用程序框架,它提供了一套用于GUI编程的类库,包括了绘图、网络、XML、数据库、多线程等模块,使开发者可以快速地开发出高质量的跨平台应用程序。Qt提供了一套用于绘图的类库,包括了QPainter、QPen、QBrush等类,使开发者可以快速地进行图形绘制。

qq_17136213的博客 918

JavaScript宏任务与微任务深度解析:JS异步执行顺序核心规则

JS 的事件循环里,主线程跑完同步代码后,不是随便从任务队列里抓任务执行的,而是有严格的顺序:先把所有微任务全执行完,清空微任务队列,才会去拿一个宏任务执行。微任务清空了,才去宏任务队列拿 setTimeout 的回调,打印 1。小索奇见过不少人踩这个坑,比如在 await 之后操作 DOM,以为能立刻拿到更新后的元素,结果发现拿的还是旧值,就是因为没搞懂 await 后续代码是微任务的缘故。下次再遇到异步代码顺序乱了,先别急着改延迟时间,先看看哪些是宏任务,哪些是微任务,按规则一推,答案就出来了。

思索的涟漪,突破自我 794

STM32 (基于HAL库) 硬件IIC任意地址读写任意字节----AT24CXX芯片

转了一圈,发现有关STM32的iic的硬件是使用比较少,基于HAL库的就更少,传统的,大家都是采用软件模拟的。有iic的协议相对复杂,同时灵活,尤其是不同的设备的I2C通信组合变化也不同,使用灵活的软件模拟也是可以理解的,在加上I2C的设备读写速度都比较低,硬件读写的优势就更无法体现,哪怕硬件I2C可以开启DMA,甚至在随机的读取少量字节的情况下。开启DMA反而效果更差。自然而然的硬件I2C就无人问津了。

qq_32273059的博客 1580

JavaScript基础——异步任务的执行顺序

JavaScript的异步任务执行顺序,异步分为微任务和宏任务!

蜡笔不新的博客 775

【ArcGIS Pro微课1000例】0022:基于DEM进行流域分析生成流域图

填洼Fill,在进行水文分析后续操作前,首先要对DEM进行填洼,创建无凹陷点的DEM。在ArcGIS中使用的是八方向流量建模(D8算法),工具位于水文分析下的【流向】。填洼需要使用水文分析工具下的【填洼】。确定输入与输出即可。

智慧空间实验室 2753

23、OpenMP常见用例与标准循环级示例详解

本文详细介绍了OpenMP的三种典型用例:循环级OpenMP、高级OpenMP和MPI+OpenMP,适用于不同层次的并行需求。重点讲解了循环级OpenMP在向量加法、流三元组和模板计算中的应用示例,并探讨了首次触摸优化、线程绑定、NUMA架构影响等关键性能优化策略。通过实际代码演示和性能分析,帮助开发者快速掌握OpenMP的基础并行化方法,并为后续高性能优化提供指导。

fanta的博客 87

js中的同步任务、异步任务 以及Promise的执行顺序

在js所有任务中,分为两种,一种是同步任务,一种是异步任务。 同步任务是指,在js的主线程中执行的任务,只有前一个任务执行完毕,下一个任务才会开始执行。 异步任务是指,不在主线程,而是在任务队列中的任务。只有主线程中的任务全部执行完毕,才会在任务队列中按顺序取出任务放入主线程再执行。 而任务队列中存放的是有了结果的异步函数 异步执行的机制就是: 1、所有同步任务都在主线程上执行 2、主线程外由一个任务队列,存放着有了结果的异步函数。 3、当主线程中的所有同步任务执行完毕,就会读取任务队列中的异

qq_57205669的博客 2908

华为OD机试新系统真题【迷宫相遇】

华为OD机试 新系统真题【迷宫相遇】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题【迷宫相遇】多语言题解。

qq_45776114的博客 770

解读同步与异步,同步任务与异步任务,JavaScript中的任务执行顺序

遇到代码,从最外层作用域层层向内剖析任务类别,如console.log()、new Promise()之类,先将这种同步任务执行,再去看他们身上是否存在微任务,如new Promise()上的then()方法、.catch()方法,如果有,先将此微任务依次放入宿主环境等待执行,所有的同步任务限制先执行完后,再执行等待中的微任务,微任务完之后执行宏任务。(简单理解同步就是有顺序的从先到后执行,前一个进程没有执行完,后一进程只有等待前面的执行完后,才可以继续执行)前一任务执行完毕后,才可以执行后一任务才。

暴躁小猿的博客 2608

【GameFramework框架内置模块】9、有限状态机(FSM)

这篇文章分享FSM有限状态机,了解过StarForce源码的同学相比都比较了解,GameFramework框架给我们抽象了一个Procedure流程的概念。这个Procedure流程可以方便的处理我们游戏从启动到游戏的各个流程阶段的处理逻辑,实现的核心就是FSM状态机,内部的Procedure流程就是不同的FsmState状态。接下来,就是FSM有限状态机的教程了。有限状态机(Finite-state machine,FSM)

优快云博客专家,Unity3D高级软件工程师,《Unity 3D从入门到实战》作者。 2533

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

async和await用法理解和快速上手 , 同步任务和异步任务顺序安排和轻松理解 , js代码执行顺序表面知道

虽然说 async 和 await 是 Promise 的语法糖 , 但是用惯了Promise 的人(我) , 还真不能超快速使用上这个语法糖 , 所以赶紧写一篇文章出来让各位了解了解这个到底怎么用在我的项目中呢 , 毕竟大家都是想用在项目里的 , 而不是说简单的学习一下语法难道只有我看了语法介绍还是不会用嘛同时 这一部分和异步任务有很大的关系 , 因为一般 await 我们是在发起请求时使用的 , 所有我们同样也会看看 js 中的同步任务和异步任务。

shaoyahu的博客 4286

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

js的同步与异步,宏任务与微任务的执行顺序

js的同步与异步,宏任务与微任务的执行顺序

qq_47548037的博客 1145

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

JavaScript同步执行、异步执行及异步事件队列中的执行顺序

首先明确两点: 1.JS 执行机制是单线程。 2.JS的Event loop是JS的执行机制,深入了解Event loop,就等于深入了解JS引擎的执行。 单线程执行带来什么问题? 在JS执行...

Wind_waving的博客 2573

js同步任务,异步任务,宏任务 和 微任务 的执行顺序

宏任务 和 微任务都是异步任务 宏任务 setTimeout setInterval 微任务 process.nextTick() promise.then() // 这个微任务一般放在nextTick队列后面如果异步队列里有nextTick队列的话 这道题帮助我们了解各种任务执行的顺序 console.log('1'); setTimeout(function() { console.log('2'); process.nextTick(function() {

xcneuq的博客 287

js回调执行顺序,同步任务与异步任务,宏任务与微任务

JS执行顺序 JS是单线程的,即一段时间只能执行一个任务。执行一段代码,js总是按照顺序执行的,只不过在执行的过程中不会等待异步任务。 同步任务与异步任务 同步任务:立即执行的任务,直接被主线程读取并执行。 异步任务:异步执行的任务,会交给任务队列去处理。异步任务又分为宏任务与微任务。 宏任务与微任务 宏任务:异步 Ajax 请求、setTimeout、setInterval、文件操作,其它宏任务。 微任务:Promise.then、.catch 和 .finally,process.nextTick,其它

weixin_46389390的博客 1207

关于js中事件循环、同步异步、宏任务和微任务存在的时候函数执行顺序的简单理解...

讲述目的:本文章针对JS萌新,是要用最简单的解释让读者能够判断在函数语句的执行顺序,不涉及关于js更深层的理解和探讨也不花费精力讲解js为什么会将执行任务有这些区分,只讨论任务的执行顺序,保护读者不被各种概念绕晕,大神可绕道。 讲述思路: 1.简单理解同步异步、宏任务和微任务 2.执行顺序判断方法 3.简单实例分析 4.稍复杂点的实例分析 正文开始: 1.简单理解同步异步、宏任务和微...

weixin_33894640的博客 175

JS同步执行、异步执行、及同步中的异步执行(promise和then)

首先明确两点: 1.JS 执行机制是单线程。 2.JS的Event loop是JS的执行机制,深入了解Event loop,就等于深入了解JS引擎的执行。 单线程执行带来什么问题? 在JS执行中都是单线程执行,所以代码的执行可以说是自上而下,如果前一段的代码出现问题,就会导致下一段代码无法执行,对于用户而言就是卡死现象,所以在JS执行机制引出了异步执行操作。 那异步能解决...

qq_37899792的博客 1万+

JS执行机制(同步和异步)

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。异步:在做这件事的同时,你还可以去处理其他事他们的本质区别:这条流水线上各个流程的执行顺序不同。

2301_76662406的博客 1215

JS【详解】在浏览器中 JS 代码的执行顺序(含异步机制、同步任务、异步任务、宏任务、微任务、Event Loop)

js 的同步任务和异步任务 js代码可以分为两种任务: 同步任务(synchronous)—— 在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务; 异步任务(asynchronous)—— 不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。 异步任务又分为宏任务与微任务: 宏任务(macrotask ) 事件/函数 浏览器环境 Node.js环境 I/O ✅ ✅ .

朝阳39的博客 899

用最简单的方式教你了解js中的异步执行顺序 微任务和宏任务

/* 执行顺序 */ // 同步主线程 => 异步线程 => 微任务 => 宏任务 => 宏任务里再循环 => 微任务 => 宏任务 /* 关键理解 */ // promise是同步的 只有resolve出去的值才是异步的 // promiss 是微任务 // setTimeout ajax异步回调 是宏任务 /* 简单实例1 */ setTimeout(function () ..

高先生的猫 197

js宏任务和微任务(异步和同步执行的顺序)探究

js宏任务和微任务(异步和同步执行的顺序)探究 目录js宏任务和微任务(异步和同步执行的顺序)探究前言一、什么是宏任务和微任务二、图例解释三、示例代码总结附:js定时器(setTimeOut)真的守时吗? 前言 js属于单线程,也就是说对于一段js代码片段,只会顺序依次执行,都是同步执行的。而实际使用过程中有些任务执行时间长,有些任务执行时间短,这会导致页面加载时间过长并且出现空白页面的现象,非常影响用户体验。为了解决这个问题产生了宏任务和微任务。 一、什么是宏任务和微任务 广义上来说 宏任务包

互联网编程爱好者 1448
上一篇: mac中如何使用vsode愉快地运行C、C++程序
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值