首页 > 其他分享 >ES6 Promise

ES6 Promise

时间:2024-04-07 17:33:57浏览次数:24  
标签:ES6 resolve console log 实例 Promise catch

ES6 Promise

陈丶陈奎宁 陈丶陈奎宁    

promise /ˈprɑːmɪs/ 承诺;许诺;保证

Promise的含义

Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件更合理、更强大。ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。

我们来模拟传统的解决方案

const url = 'https://jsonplaceholder.typicode.com/todos'
// 它是由浏览器提供的,不是JavaScript原生的
const xhr = new XMLHttpRequest()
let btnDom = document.getElementById('btn')
const handler = function() {
  
}
// 绑定点击事件
btnDom.onclick = function () {
  xhr.onreadysrtatechange = fucntion() {
    // readyState表示请求/响应过程中当前的活动阶段0 1 2 3 4
    if (xhr.readyState === 4) {
      if (xhr.status >=200 && xhr.status <= 300) {
        const data = xhr.responseText;
        // 拿到数据后如果还需要在做发送请求的动作
        // 我们就需要在onreadysrtatechange的回调中继续写代码
        // 这样代码就会嵌套很多层 也被称为回调地狱
      } else {
        console.log('Response was unsuccessful ' + xhr.status)
      }
    }
  }
  xhr.open('get', 'http://www.baidu.com')
  xhr.setRequesetHeader('customHeader', 'customHeaderValue')
  // send的参数不能为空,get请求传递null
  xhr.send(null)
}

所谓Promise,简单来说就是一个容器,里面保存着某个未来才会结束的事件的结果(通常是一个异步操作)

它有两个特点:

  1. 对象的状态不受外界的影响。它有3个状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)只有异步操作的结果「 (resolve, reject) => { 异步操作返回结果后调用 resolve(value) 或者 reject(err) } 」可以决定当前是哪种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来。
  2. 一旦状态发生改变,就不会再变。任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从 pending 到 fulfilled 和 从 pending 到 rejected

它也有一些缺点:

  1. 无法取消Promise,一旦新建它会立即执行,无法中途取消。
  2. 如果不设置回调函数「.then() 和 .catch() 」Promise内部抛出的错误,不会反应到外部。

ES6规定,Promise是一个构造函数,用来生成Promise实例

const promise = new Promise(function(resolve, rejecte) {
  // some code
  if (/*异步操作成功*/) {
    resolve(value)
  } else {
    reject(error)
  }
})

Promise构造函数 接受一个函数作为参数,该函数 的 两个参数 分别是 resolve 和 reject ,它们又是两个函数,由JavaScript引擎提供,不用自己部署。

resolve函数的作用是,将 Promise 对象的状态 从 pending 变为 resolved 。在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。

reject函数的作用是,将 Promise 对象的状态 从 pending 变为 rejecetd 。在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用 then 方法指定 resolved 状态 和 rejected 状态 的回调函数

promise.then(res => {
  // success
}, err => {
  // error
)

then方法接受两个回调函数作为参数。

第一个回调函数是Promise对象的状态变为resolved时调用
第二个回调函数是Promise对象的状态变为rejected时调用

这两个函数都是可选的,不一定要提供。它们都接受Promise对象传出的值( res 和 err )作为参数。

function timeout(ms) {
  // 返回Promise实例对象 用来.then后续的操作
  return new Promise((resolve, rejecte) = > {
    setTimeout(() => {
      resolve('success setTimeout ' + ms)
    }, ms)
    // 简写
    setTimeout(resolve, ms)
  })
}
// 过了ms后,Promise实例的状态变为resolved
// 就会触发.then方法绑定的回调函数
timeout(3000).then(res => {
  console.log(res) // success setTimeout 3000
})

Promise 新建后就会立即执行

let promise = new Promse((resolve, reject) => {
  console.log('Promise')
  resolve()
})
promise.then(() => {
  console.log('resolved')
})
console.log('hello')
// 'Promise'
// 'hello'
// 'resolved'

异步加载图片的例子

function loadImage(url) {
  return new Promise((resolve, reject) => {
    const image = new Image()
    image.onload = function() {
      resolve(image)
    }
    image.onerror = function() {
      reject(new Error('Could not load image at ' + url))
    }
    image.src = url
  })
}
// 调用异步加载图片的方法
loadImage('https://p7.itc.cn/images01/20201104/87d0aa991d454770813dcb01260c2ed0.jpeg')
.then(res => {
  // res <img src='https://p7.itc.cn/images01/20201104/87d0aa991d454770813dcb01260c2ed0.jpeg'>
  console.log('res: ', res)
  // 可以插入到页面某个位置
}).catch(error => {
  console.log('error: ', error)
})

用Promise实现Ajax操作的例子

const getJSON = function(url) {
  return new Promise((resolve, rejected) => {
    const jsx = new XMLHttpRequest()
    const handler = function() {
      if (this.readyState !== 4) return
      if (this.status >= 200 && this.status <= 300) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    jsx.open('get', url)
    jsx.onreadystatechange = handler;
    jsx.responseType = 'json'
    jsx.send(null) 
  })
}
getJSON('/post.json').then(json => {
  console.log(json)
}, error => {
  console.log(error)
})

Promise.prototype.then()

Promise实例具有then方法,它的then方法是定义在原型对象 Promise.prototype 上的

then方法如果返回一个新的Promise实例(不是原来那个实例)因此可以采用链式写法,即then方法后面可以再调用另一个then方法

getJSON('/post.json').then(json => {
  return getJSON('/post.json?id=' + json.id)
  // return getJSON(json.conmmentUrl)
}, error => {
  console.log(error)
}).then(content => {
  console.log(content)
})

Promise.prototype.catch()

一般来说,不要在then方法里面定义 rejected 状态的回调函数,总是使用catch方法

因为catch这种写法不仅可以捕获到 rejected 状态的错误,还可以捕获前面then方法执行中的错误,并且也更接近嗯同步的写法「try / catch」

跟传统的 try / catch 代码块不同的是,如果没有使用 catch 方法指定错误处理的回调函数, Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。通俗的说法就是'Promise 会吃掉错误'

const someThing = function() {
  return new Promise((resolve, reject) => {
    // 这行会报错,因为x没有声明
    resolve(x + 2)
  })
}
someThing().then(res => {
  console.log('everything is great!')
}) 
setTimeout(() => console.log(123), 2000)
// ReferenceError: x is not defined
// 123

上述代码控制台会报出错误,但是不会退出进程、终止脚本执行,2s后还是会输出123

这就是说 Promise 内部的错误不会影响到 Promise 外部的代码,造成 'Promise会吃掉错误'

显然这样的行为是不可取的

所以,也总是建议 Promise 对象后面要跟 catch 方法,这样可以处理 Promise 内部发生的错误。

Promise.prototype.finally()

finally方法用于不管 Promise 对象最后的状态如何,都会执行的操作。

promise
  .then(result => { ... })
  .catch(err => { ... })
  .finally(() => { ... })

上面代码中,不管 promise 最后的状态,在执行完 then 方法 或 catch 方法的回调函数以后,都会执行finally方法指定的回调函数。

finally方法的回调函数不接受任何参数,这意味着没有办法知道前面的 Promise 状态到底是 resolved 还是 rejected ,这表明,finally方法里的操作,应该是与状态无关的,不依赖于Promise执行的结果。

Promise.all()

all方法将拥有多个 Promise 实例,包装成一个新的 Promise 实例

// p1 p2 p3 都是Promise实例 如果不是Promise实例 
// 将会调用Promise.resolve()方法转为Promise实例
const p = Promise.all([p1, p2, p3])

p的状态有两种情况

  1. 只有 p1 p2 p3 的状态都变成 fulfilled ,p的状态才会变成 fulfilled 此时,p1 p2 p3的返回值组成一个数组,传递给p的回调函数
  2. 只要 p1 p2 p3 之中有一个状态被 rejected ,p的状态就会变成 rejected ,此时第一个被 rejected 的实例的返回值,会传递给p的回调函数
const promises = [2, 3, 5, 7, 11, 13].map((id) => {
  return getJSON('post/' + id + '.json')
})
Promises.all(promises)
  .then(posts => { ... })
  .catch(reason => { ... })

catch方法返回的是一个新的 Promise 实例(该实例执行完catch方法后,也会变成resolved),catch后面可以继续链式调用then方法,结合all方法看下

const p1 = new Promise((resolve, reject) => {
  resolve('hello')
})
.then(result => result)
.catch(e => e)

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了')
})
.then(result => result)
.catch(e => e)

Promise.all([p1, p2])
.then(result => console.log(result))
// 不会触发catch方法
.catch(e => console.log(e))
// ['hello', Error:报错了]

因为catch方法返回的是一个新的Promise实例且resolved状态的,所以不会出发all方法的catch方法

Promise.race() 竞赛

race方法同样是将多个Promise实例,包装成一个新的Promise实例

const p = Promise.race([p1, p2, p3])

上面代码中,只要 p1 p2 p3 之中有一个实例率先改变状态,p的状态就跟着改变,那个率先改变的Promise实例的返回值,就传递给p的回调函数

下面的例子,如果一个请求指定时间内没有获取到结果,就抛出超时错误

const p = Promise.race([
  fetch(url),
  setTimeout(() => {
    reject(new Error('time out'))
  }, 5000)
])
p.then(data => {
  console.log(data) // 拿到请求数据data
})
.catch(err => {
  console.log(err) // time out
})

 

发布于 2024-01-04 22:19・IP 属地上海

标签:ES6,resolve,console,log,实例,Promise,catch
From: https://www.cnblogs.com/sexintercourse/p/18119526

相关文章

  • 深入解析ES6中的promise
    深入解析ES6中的promise 作者|Jeskson来源|达达前端小酒馆什么是PromisePromise对象是用于表示一个异步操作的最终状态(完成或失败)以及其返回的值。什么是同步,异步同步任务会阻塞程序的执行,如alert,for异步任务不会阻塞程序的执行,如setTimeou使用Promise,then,cat......
  • Promise详解与自定义封装
    文章目录概要一、Promise详解1.构造函数1.1语法1.2参数1.3返回值2.属性2.1[[PromiseState]]2.2[[PromiseResult]]3.方法3.1Promise.prototype.then3.1.1语法3.1.2参数3.1.3返回值3.2Promise.prototype.catch3.2.1语法3.2.2参数3.2.3返回值3.3Promis......
  • C++多线程:async、future、packaged_task、promise、shared_future的学习与使用(九)
    1、异步任务线程异步线程的概念:异步:就是非同步,同步就是必须一个一个的执行,异步可以两个事情一起干异步线程:异步线程就相当于把非关联的两件事分开找两个线程去执行,而分开的那个就是异步线程举例:例如登录信息,用户登录完毕主线程肯定是需要去及时响应用户的请求的,而系统设......
  • ES6中模块化详解
    前言因为ES6中的模块化是将来,所以就必须有必要好好的了解一下,学习一下,这篇文章就简单总结一下ES6中模块的概念,语法和用法。纯属个人总结,不喜勿喷。下面我将通过a.js、b.js和c.js三个文件把ES6的知识点穿起来。默认导出导出语法:exportdefault默认导出的成员样例代码a.js:/......
  • ES6 exports 与 import 使用
    基本用法基本用法exportsNamedexports(命名导出)Defaultexports(默认导出)exportdefault与export的区别:Combinationsexports(混合导出)Re-exporting(别名导出)ModuleRedirects(中转模块导出)Importexport与import的复合写法注意基本用法模块导入导......
  • 回调地狱--promise与async+await
    一、回调地狱首先了解两个概念,什么是回调函数?什么是异步任务?1.1回调函数当一个函数作为参数传入另一个参数中,并且它不会立即执行,只有当满足一定条件后该函数才可以执行,这种函数就称为回调函数。(它是作为参数传递给另一个函数的函数)我们熟悉的定时器和Ajax中就存在有回......
  • ES6数组中删除指定元素
    ES6数组中删除指定元素 知识点:ES6从数组中删除指定元素findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。arr.splice(arr.findIndex(item=>item.id===data.id),1) http://louiszhai.github.io/2017/04/28/array/1:js中的spli......
  • 在 ES6 中,for...of 和 for...in 讲解
    在ES6中,for...of 和 for...in 是两种用于遍历不同数据结构的循环结构。它们各自有不同的用途和适用场景。for...offor...of 循环用于遍历可迭代对象(包括Array、Map、Set、String、TypedArray、函数的arguments对象等等)的值。语法for(variableofiterable){/......
  • 手写Promise
    1.建立基础的构造函数需求基于Promises/A+(promisesaplus.com),我们需要实现:promise有三个状态:pending(未完成),fulfilled(完成),orrejected(拒绝)。初始状态为pending,且状态结束只能从pending改为fulfilled或者rejected,promise的状态改变为单向且不可逆。promise接......
  • 【JS】手写Promise.all
    注意判断参数是否为空时,不能只根据length判断,因为只要是可迭代对象都可以用length。js有哪些内置可迭代对象可以看另一篇文章:JS内置可迭代对象。如何变为可迭代对象可以看其他两篇文章:Object.definePropery和使如下代码成立:var[a,b]={a:1,b:2}防止用户传入非P......