发布于

Promise

Authors
  • avatar
    Name
    田中原
    Twitter

javascript 基础概念理解

javascript 引擎是基于单线程事件循环的概念构建的,同一时间只允许一个代码块执行。

**事件循环:**负责监控代码执行并管理任务队列,每当 javascript 引擎中的一段代码结束执行,事件循环会执行任务队列中的下一段代码

**任务队列:**每当一段代码准备执行时,都会被添加到任务队列,队列的任务会从第一一直执行到最后一个。

**事件模型:**通过用户的点击按钮等操作触发类似 onclick 这样的事件,然后它会向任务队列添加一个新任务来响应用户的操作。这是javascript 种最基础的异步编程形式

**回调模式:**等待被调用的函数作为参数传入到异步代码中

Promise

Promise 相当于异步操作的结果占位符

// readFile 为异步函数,并在未来某个时刻完成
let promise = readFile('example.txt')

readFile 函数返回一个承诺(Promise 对象),未来这个承诺的操作完全取决于 Promise 的生命周期

promise 生命周期

promise 的三种状态:

  • pending Promise 处于进行中,操作尚未完成
  • Fulfilled Promise 异步操作成功完成
  • Rejected Promise 异步操作未能成功完成

所有的 Promise 都有 then() 方法,接受两个参数:1.完成函数即当状态变为 fulfilled 是调用的函数,异步操作的相关附加数据会传递给这个函数。 2.拒绝函数,即当状态变为 rejected 时调用的函数,所有失败状态相关的附加数据都会传递给这个函数

thenable对象: 如果一个对象实现了 then() 方法,那这个对象我们称之为 thenable 对象

promise 的 then() 方法 catch() 方法

let promise = readFile('example.txt')

promise.then(
  () => {
    // 完成
  },
  () => {
    // 拒绝
  }
)

// 以下两种写法等价
promise.then(null, () => {
  // 拒绝
})
promise.catch(() => {
  //拒绝
})

创建一个未完成的 promise

用 Promise 的构造函数可以创建新的 Promise, 构造函数接受一个参数:包含初始化 Promise 代码的执行器函数。执行器函数接受两个参数:resolve 函数 和 reject 函数

let fs = require('fs')
function readFile(filename) {
  return new Promise((resolve, reject) => {
    fs.readFile(filename, (err, contents) => {
      if (err) {
        reject(err)
      }
      resolve(contents)
    })
  })
}
let promise = readFile('example.txt')

promise
  .then((contents) => {
    // 完成
  })
  .catch(() => {
    // 拒绝
  })

理解promise 执行过程

以下代码的输出是什么?

function promise() {
  return new Promise((resolve, reject) => {
    console.log('Promise')
    setTimeout(() => {
      resolve('promise resolve')
      console.log('resolve')
    }, 2000)
  })
}

let aPromise = promise()
aPromise.then((data) => {
  console.log(data)
})
console.log('hello')

promise 的工作原理:Promise 执行器会立即执行,而reject 或 resolve 是在执行器完成之后添加到任务队列的队尾

创建已处理的 Promise

// Promise.resolve() 只接受一个参数并返回一个完成的 Promise,并且拒绝程序永远不会被调用
let promise = Promise.resolve(43)
promise.then((value) => {
  console.log(value) // 43
})

// Promise.reject() 创建一个已拒绝的 Promise,并且完成处理程序永远不会被调用
let promise = Promise.reject(43)
promise.catch((value) => {
  console.log(value) // 43
})

非 Promise 的 Thenable 对象

拥有 then() 方法并且接受 resolve 和 reject 这两个参数的普通对象

let thenable = {
  then(resolve, reject) {
    resolve(43)
  },
}
let p1 = Promise.resolve(thenable)

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

如果不确定某个对象是不是 Promise 对象,那么可以根据预期的结果将其传入 Promise.resolve() 中 或 Promise.reject() 中,如果它是 Promise 对象,则不会有任何变化

执行错误

执行器会捕获所有抛出的错误,但只有当拒绝处理程序存在是才会记录执行器中抛出的错误,否则错误就会被忽略掉。每个执行器都隐含一个 try-catch 块:

let promise = new Promise((resolve, reject) => {
  try {
    throw new Error('Explosion')
  } catch (e) {
    reject(e)
  }
})

promise.catch((error) => {
  console.log(error.message)
})

全局的Promise 拒绝处理

串联 Promise

每次调用 then() 方法或 catch() 方法是实际上创建并返回了另一个 Promise,只有当第一个 Promise 完成或拒绝后,第二个才会被解决。务必在 Promise 链的末尾留有一个拒绝处理程序已确保能够正确处理所有可能发生的错误

Promise 链重要特性:

  • 可以捕获错误
  • 可以给下游 Promise 传递参数
  • 可以在 Promise 链中返回 Promise
let p1 = new Promise((resolve, reject) => {
  resolve(42)
})

pl.then((value) => {
  console.log(value) // 42
  throw new Error('Boom')
})
  .catch((err) => {
    console.log(err.message) // Boom
    return new Promise((resolve, reject) => {
      resolve(43)
    })
  })
  .then((value) => {
    console.log(value) // 43
    return value + 2
  })
  .then((value) => {
    console.log(vaule) // 45
  })

响应多个 Promise

**promise.all() **方法 只接收一个参数,该参数是一个含有多个受监视 Promise 的可迭代对象。只有当可迭代对象中所有的 Promise 都完成后,返回的 Promise 才会被完成

let p1 = new Promise((resolve, reject)=>{
    resolve(42)
})
let p2 = new Promise((resolve, reject)=>{
    resolve(43)
})
let p3 = new Promise((resolve, reject)=>{
    resolve(44)
})
let p4 = Promise.all([p1, p2, p3])   // 传入可迭代对象

p4.then((value)=>{  // value 返回一个数组
  console.log(Array.isArray(value))   // true
  console.log(value[0])  42
  console.log(value[1]) 43
  console.log(value[2]) 44
})

所有传入的 Promise 只要有一个被拒绝,那么返回的 Promise 没等所有 Promise 都完成就立即被拒绝

let p1 = new Promise((resolve, reject) => {
  resolve(42)
})
let p2 = Promise.reject(43)
let p3 = new Promise((resolve, reject) => {
  resolve(44)
})
let p4 = Promise.all([p1, p2, p3]) // 传入可迭代对象

p4.catch((value) => {
  // value 并非数组
  console.log(Array.isArray(value)) // false
  console.log(value) // 43
})

**promise.race() **方法,接受含有多个受监视 Promise 的可迭代对象作为唯一参数并返回一个Promise ,但只要有一个 Promise 被解决返回的 Promise 就被解决。实际上,传给 Promise.race() 方法会进行竞选,以决出哪一个先被解决,如果先解决的是已完成的 Promise,则返回已完成的 Promise; 如果先解决的是已拒绝的 Promise, 则返回已拒绝 Promise。

let p2 = new Promise((resolve, reject) => {
  reject(43)
})
let p1 = Promise.resolve(42)
let p3 = new Promise((resolve, reject) => {
  resolve(44)
})
let p4 = Promise.race([p2, p1, p3]) // 传入可迭代对象

p4.then((value) => {
  console.log(value) // 42
}).catch((err) => {
  console.log(err) // 不会输出
})

自 Promise 继承

await 语法

async:用在函数关键字前面,表示该函数以异步模式运行,代替生成器

await:表示调用的函数应该返回一个 Promise,代替 yield 关键字

async function creatAsync() {
  let a = await new Promise((resolve) => {
    setTimeout(() => {
      resolve(42)
    }, 2000)
  })
  return Promise.resolve(a)
}

async function dosomething() {
  let a = await creatAsync()
  creatAsync.then(() => {})
  console.log(a)
}
dosomething()

// 错误捕获
async function creatAsync() {
  let a = await new Promise((resolve) => {
    setTimeout(() => {
      resolve(42)
    }, 2000)
  })
  return Promise.reject(new Error('Error'))
}

async function dosomething() {
  try {
    let a = await creatAsync()
    console.log(a)
  } catch (e) {
    console.log(`err: ${e}`)
  }
}
dosomething()