Promise从入门到放弃

Promise的理解与使用

Promise的理解

Promise是什么

抽象的表达就是:Promise 是异步编程的一种解决方案。

而具体点的表达就是:

  1. 在语法上:Promise 是一个构造函数;

  2. 在功能上:Promise 对象是用来封装一个异步操作并可以从中获取结果;

Promise的状态

Promise 只有三种状态,分别为初始状态(pending)、成功状态(fulfilled / resolved)、失败状态(rejected)。

  1. pending(初始状态): 初始状态,既不是成功,也不是失败状态;

  2. fulfilled / resolved(已成功): 意味着操作成功完成;

  3. rejected(已失败): 意味着操作失败;

而在这三种状态之间,Promise 对象的状态改变只有两种情况,分别是:

  1. 从初始状态(pending)变为成功状态(fulfilled / resolved);

  2. 从初始状态(pending)变为失败状态(rejected);

只要上面这两种情况发生了,状态就凝固了,不会再变了,会一直保持这个结果。

一个 Promise 对象只能改变一次状态,无论最后是变成成功还是失败,都会有一个数据结果。

为什么要用Promise

使用 Promise 的原因有如下两点:

  1. 指定回调函数的方式更加灵活

  2. 支持链式调用,可以解决回调地狱的问题

那么回调地狱又是什么呢?

回调地狱即回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调的执行条件。

而回调地狱十分不便于开发者阅读,也不利于进行异常处理。因此,使用 Promise 的链式调用可以解决此类问题。

Promise的基本使用

Promise 的基本书写语法大致如下:

function fn() {
  return new Promise((resolve, reject) => {
    成功时调用 resolve(value)
    失败时调用 reject(error)
  })
}
fn().then(成功函数1, 失败函数1).then(成功函数2, 失败函数2)
let p = new Promise((resolve, reject) => {
  setTimeout(() => {
    let time = Date.now()
    console.log(time)
    if (time % 2 === 1) {
      resolve(time)
    } else {
      reject(time)
    }
  }, 1000)
})
p.then((value) => {
  console.log('成功的回调' + value)
}, (error) => {
  console.log('失败的回调' + error)
})

Promise的API

Promise.prototype.then

Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。该方法返回一个新的 promise 对象。

它的作用是为 Promise 实例添加状态改变时的回调函数。then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

let p1 = new Promise((resolve, reject) => {
  ...
})
p1.then(value => {...}, error => {...})

Promise.prototype.catch

Promise.prototype.catch() 方法是 .then(null, rejected) 或 .then(undefined, rejected) 的别名,用于指定发生错误时的回调函数。

let p1 = new Promise((resolve, reject) => {
  reject('error')
})
p1.then(value => {
  console.log(value)
}).catch(error => {
  console.log(error)
})

Promise.resolve

Promise.resolve() 返回一个成功 Promise 对象。

// value: 成功的数据或 Promise 对象
Promise.resolve(value)
let p1 = Promise.resolve('p1')
// 等价于
let p1 = new Promise(resolve => {
  resolve('p1')
})

Promise.reject

Promise.reject() 返回一个失败的 Promise 对象。

// error: 失败的原因
Promise.reject(error)
let p2 = Promise.reject('error')
// 等价于
let p2 = new Promise((resolve, reject) => {
  reject('error')
})

Promise.all

Promise.all() 方法用于将多个 Promise 实例包装成一个新的 Promise 实例。传入的参数为数组形式。

该 Promise 对象在数组参数对象里所有的 Promise 对象都成功的时候才会触发成功,一旦有任何一个数组参数对象里面的 Promise 对象失败则立即触发该 Promise 对象的失败。

// promise1 、promise2 、 promise3 都成功才会调用成功函数,否则调用失败函数
Promise.all([promise1, promise2, promise3, ...]).then(成功函数, 失败函数)
let p1 = new Promise((resolve, reject) => {
  resolve('p1')
})
let p2 = new Promise((resolve, reject) => {
  resolve('p2')
})
let p3 = new Promise((resolve, reject) => {
  reject('p3')
})

let pAll1 = Promise.all([p1, p2, p3]).then(value => {
  console.log(value)
}, error => {
  console.log(error)    // p3
})

let pAll2 = Promise.all([p1, p2]).then(value => {
  console.log(value)    // (2) ["p1", "p2"]
}, error => {
  console.log(error)
})

Promise.all() 返回一个新的 Promise, 只有所有的 Promise 都成功才成功, 只要有一个失败了就直接失败。

Promise.race

Promise.race() 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。传入的参数为数组形式。

race 翻译过来大致就是“比赛”的意思。因此,第一个完成的 Promise 的结果状态就是最终的结果状态。

// 如果 promise1 为成功状态并先完成,那么最终将执行成功函数;如果 promise1 为失败状态并先完成,那么最终将执行失败函数。
Promise.race([promise1, promise2, promise3, ...]).then(成功函数, 失败函数)
let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p1')
  }, 1000)
})
let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p2')
  }, 2000)
})
let p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('p3')
  }, 3000)
})

Promise.race([p1, p2, p3]).then(value => {
  console.log(value)    // p1
}, error => {
  console.log(error)
})

Promise.allSettled

Promise.allSettled() 方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是 fulfilled 还是 rejected,包装实例才会结束。

该方法返回的新的 Promise 实例,一旦结束,状态总是 fulfilled,不会变成 rejected。状态变成 fulfilled 后,Promise 的监听函数接收到的参数是一个数组,每个成员对应一个传入 Promise.allSettled() 的 Promise 实例。

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("hello")
  }, 1000)
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("error")
  }, 1000)
})

let res = Promise.allSettled([p1, p2])
console.log(res)    // Promise {<pending>}

手写Promise - 待完善

相关问题与题目

如何改变Promise的状态

Promise 状态的改变除了上面所说的从 pendding 变为 resolved 和 pendding 变为 rejected 之外,还有一种可以改变状态,那就是抛出异常,会从 pendding 就会变为 rejected。

let p = new Promise((resolve, reject) => {
  throw new Error('error')
})
p.then(
  value => {},
  error => {
    console.log(error)    // Error: error
  }
)

改变Promise状态和指定回调函数的谁先谁后

都有可能,一般情况下先指定回调函数然后再改变的状态,但是也可也先改变状态然后再指定回调函数

如下两种方法可以先改变状态然后再指定回调函数:

  1. 在执行器中直接调用 resolve() / reject()

  2. 添加 setTimeout 延迟更长的时间调用 .then()

// 先指定的回调函数,并保存当前指定的回调函数。然后改变的状态,再执行异步回调函数
let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p1')
  }, 1000)
})
p1.then(value => {
  console.log(value)
}, error => {
  console.log(error)
})
// 先改变的状态,然后指定回调函数,并异步执行回调函数
let p2 = new Promise((resolve, reject) => {
  resolve('p2')
})
p2.then(value => {
  console.log(value)
}, error => {
  console.log(error)
})
// 先改变的状态,然后指定回调函数,并异步执行回调函数
let p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p3')
  }, 1000)
})
setTimeout(() => {
  p3.then(value => {
    console.log(value)
  }, error => {
    console.log(error)
  })
}, 1200)

.then()返回的新Promise的结果状态由什么决定

  1. 如果返回的是非 Promise 的任意值, 新 Promise 变为 resolved, value 为返回的值;

  2. 如果返回的是另一个新 Promise, 那么这个 Promise 的结果就会成为新 Promise 的结果 ;

  3. 如果抛出异常, 新 Promise 变为 rejected, error 为抛出的异常;

new Promise((resolve, reject) => {
  resolve('hh')
}).then(value => {
  console.log('demo1,' + value)    // demo1,hh
}, error => {
  console.log('demo1,' + error)
}).then(value => {
  console.log('demo2,' + value)    // demo2,undefined
}, error => {
  console.log('demo2,' + error)
})
new Promise((resolve, reject) => {
  resolve('hh')
}).then(value => {
  console.log('demo1,' + value)    // demo1,hh
  return 1
}, error => {
  console.log('demo1,' + error)
}).then(value => {
  console.log('demo2,' + value)    // demo2,1
}, error => {
  console.log('demo2,' + error)
})
new Promise((resolve, reject) => {
  resolve('hh')
}).then(value => {
  console.log('demo1,' + value)    // demo1,hh
  return Promise.resolve('3')
}, error => {
  console.log('demo1,' + error)
}).then(value => {
  console.log('demo2,' + value)    // demo2,3
}, error => {
  console.log('demo2,' + error)
})
new Promise((resolve, reject) => {
  resolve('hh')
}).then(value => {
  console.log('demo1,' + value)    // demo1,hh
  return Promise.reject('4')
}, error => {
  console.log('demo1,' + error)
}).then(value => {
  console.log('demo2,' + value)
}, error => {
  console.log('demo2,' + error)    // demo2,4
})
new Promise((resolve, reject) => {
  resolve('hh')
}).then(value => {
  console.log('demo1,' + value)    // demo1,hh
  throw 5
}, error => {
  console.log('demo1,' + error)
}).then(value => {
  console.log('demo2,' + value)
}, error => {
  console.log('demo2,' + error)    // demo2,5
})
(0)

相关推荐

  • 异步编程之Promise

    一.Promise 1. .then 和.catch · resolved 状态的Promise 会回调后面的第一个.then · rejected 状态的Promise 会回调后面的第一个.catc ...

  • 原以为很简单,结果这道 Promise 面试题让我失眠好一会

    来自:掘金,作者:ITEM 链接:https://juejin.cn/post/6945319439772434469 先把罪魁祸首挂在这里给大家群殴 

  • Promis.then()

    Promis.then()

  • ES6语法之异步编程-Promise

    Promise 分解异步操作,避免回调地狱 //1.promise //分解异步操作,避免回调地狱 function testMise(value) { //resolve成功后回调 //reject ...

  • ES6---async, await, promise 综合例子

    ES6---async, await, promise 综合例子 new Promise(主线程代码).then(成功微任务, 失败微任务); sync---替代promise await---替代t ...

  • 异步编程终极解决方案async-await

    一.async/await · async function 是Promise 的语法糖封装  · 异步编程的终极方案– 以同步的方式写异步 · await 关键字可以"暂停"as ...

  • 13.Promise

    Promise介绍 Promise是ES6引入的异步编程的新解决方案.语法上Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果. Promise构造函数:Promise(exc ...

  • 关于javascript中的promise的用法和注意事项

    一.promise描述 promise是javascript中标准的内置对象,用于表示一个异步操作的最终状态(是失败还是成功完成)及其结果值.它让你能够把异步操作最终成功或者失败的原因和响应的处理程序 ...

  • promise的常用情况

    因为js是单线程的,所以一旦代码中有报错,就不会执行下面的了,如下333就未打印 console.log(111) throw Error(222) console.log(333) 好像与promi ...

  • CS0: ChIPseq从入门到放弃

    接下来要出一个ChIPseq系列,讲一讲ChIPseq和我的ChIPseeker包,从入门到放弃是我自己的个人写照.我做ChIPseq总共也就3个月的时间,做的事情并不多,在一知半解的情况下写下了Ch ...

  • IF从入门到放弃

    IF函数作为大家最早接触的方式,语法也比较简单,但是你真的了解她吗? 今天小编就开一个专题,专门聊聊IF函数,本专题适合静下心想学习的同学. 沏上一杯茶,我们开始! 我们首先来解读一下IF函数的语法, ...

  • DIY从入门到放弃:为什么电源线五颜六色

    如果DIY让你觉得头疼,说明你的思路是正确的. 注意过电源线的玩家会发现,只要没有定制线材,电源输出线的颜色是不一样的,五颜六色的看起来还是挺有意思. 主板的供电线是五颜六色的 且慢,你有没有发现,C ...

  • 红蓝对抗钓鱼篇之从入门到放弃

    文章来源:freebuf 文章作者:yanmie 在大型企业边界安全做的越来越好的情况下,不管是APT攻击还是红蓝对抗演练,钓鱼和水坑攻击被越来越多地应用. 钓鱼往往需要免杀技术的支撑,但本章只讲述钓 ...

  • DIY从入门到放弃:RAID磁盘阵列你该用吗?

    如果DIY让你觉得头疼,说明你的思路是正确的. 你是不是也想有个磁盘阵列?(图片源自miracomosehace) 关注存储且对数据安全比较重视的玩家一定听说过RAID,也知道RAID是磁盘阵列的意思 ...

  • 键盘车神养成!拟真赛车从入门到放弃!赛车游戏推荐

    键盘车神养成!拟真赛车从入门到放弃!赛车游戏推荐

  • 「操控杆头」球道木起球难入门即放弃?学会这招轻松起球!

    球道木起球是很多初学球道木的球友最"头疼"的事情,杆身长.杆面倾角小.容错性相对较低......想过放弃,但球道木带来的距离确实能帮助减杆,怎样才能轻松起球.打好球道木呢? 扫击是 ...

  • VBA,从入门到放弃

    这个碎片化的信息满天飞的时代,多多少少阻碍了我们从高处看问题,以及系 统化思考的能力.为此,A哥把把教程给大家整理了一下,希望大家能对更系统地学习VBA.当然,如果你要专注了解某一块信息,也可以直接跳 ...

  • 《青年图摘0626!》从入门到放弃

     2021-06-26  [1]这特效,啧啧 [2]心中有她,哪里都是她 [3]都是一代人,转变的原因是智能手机的普及,导火索是微信公众号. [4]请假来参加车展的程序员小王 [5]天怎么黑了? [6 ...