"Code tailor",为前端开发者提供技术相干资讯以及系列根底文章,微信关注“小和山的菜鸟们”公众号,及时获取最新文章。

前言

在开始学习之前,咱们想要告诉您的是,本文章是对阮一峰《ECMAScript6 入门》一书中 "Promise" 章节的总结,如果您已把握上面常识事项,则可跳过此环节间接进入题目练习

  • 什么是 Promise?
  • Promise 如何创立和应用
  • 呈现原由及优缺点

如果您对某些局部有些忘记, 曾经为您筹备好了!

学习链接

Promise 的学习

汇总总结

概念

一个 Promise 对象代表一个在这个 Promise 被创立进去时不肯定已知的值。它让您可能把异步操作最终的胜利返回值或者失败起因和相应的处理程序关联起来。 这样使得异步办法能够像同步办法那样返回值:异步办法并不会立刻返回最终的值,而是会返回一个 Promise,以便在将来某个时候把值交给使用者。

三种状态:

  • 待定(pending): 初始状态,既没有被兑现,也没有被回绝。
  • 已兑现(fulfilled): 意味着操作胜利实现。
  • 已回绝(rejected): 意味着操作失败。

创立和应用

const promise1 = new Promise((resolve, reject) => {  setTimeout(() => {    resolve('foo')  }, 300)})promise1  .then((value) => {    console.log(value) // expected output: "foo"  })  .catch((error) => {    //产生谬误时调用    console.log(error)  })  .finally(() => {    //无论正确或谬误,都会执行    console.log('执行结束')  })console.log(promise1)// expected output: [object Promise]
  • new Promise (executor)
创立一个新的 Promise 对象。该构造函数次要用于包装还没有增加 promise 反对的函数。参数(executor)为两个:resolve 函数,运算胜利实现时调用;reject 函数,出错时调用
const promise1 = new Promise((resolve, reject) => {  setTimeout(() => {    resolve('foo')  }, 300)})promise1.then((value) => {  console.log(value)  // expected output: "foo"})console.log(promise1)// expected output: [object Promise]

罕用办法

  • Promise.all(iterable)
Promise.all() 办法接管一个 Promise 的 iterable 类型(注:ArrayMapSet 都属于 ES6iterable 类型)的输出,并且只返回一个 Promise 实例。resolve 回调执行是在所有输出的 Promiseresolve 回调都完结,或者输出的 iterable 里没有 Promise 了的时候。reject 回调执行是只有任何一个输出的 Promisereject 回调执行或者输出不非法的 Promise 就会立刻抛出谬误,并且 reject 的是第一个抛出的错误信息。
const promise1 = Promise.resolve(3)const promise2 = 42const promise3 = new Promise((resolve, reject) => {  setTimeout(resolve, 100, 'foo')})Promise.all([promise1, promise2, promise3]).then((values) => {  console.log(values)})// expected output: Array [3, 42, "foo"]
  • Promise.allSettled(iterable)
Promise.allSettled()办法返回一个在所有给定的 Promise 都曾经 fulfilledrejected 后的 Promise,并带有一个对象数组,每个对象示意对应的 Promise 后果。当您有多个彼此不依赖的异步工作胜利实现时,或者您总是想晓得每个 Promise 的后果时,通常应用它。
const promise1 = Promise.resolve(3)const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'))const promises = [promise1, promise2]Promise.allSettled(promises).then((results) =>  results.forEach((result) => console.log(result.status)),)// expected output:// "fulfilled"// "rejected"
  • Promise.race(iterable)
Promise.race(iterable) 办法返回一个 Promise,一旦迭代器中的某个 Promise 解决或回绝,返回的 Promise 就会解决或回绝。
const promise1 = new Promise((resolve, reject) => {  setTimeout(resolve, 500, 'one')})const promise2 = new Promise((resolve, reject) => {  setTimeout(resolve, 100, 'two')})Promise.race([promise1, promise2]).then((value) => {  console.log(value)  // Both resolve, but promise2 is faster})// expected output: "two"

呈现原由及优缺点

长处:

  • 状态扭转后,就不会再变,任何时候都能够失去这个后果
  • 能够将异步操作以同步操作的流程表达出来,防止了层层嵌套的回调函数
  • 肯定水平上解决了回调天堂的可读性问题

毛病:

  • 无奈勾销 promise
  • 当处于 pending 状态时,无奈得悉目前停顿到哪一个阶段
  • 代码冗余,有一堆工作时也会使语义不清晰

异步 callback 带来的”回调天堂“,逻辑凌乱,耦合性高,可读性差催生了 Promise,更多常识请点击这里JavaScript 的异步发展史

题目自测

一:以下代码最初输入什么?

const first = () =>  new Promise((resolve, reject) => {    console.log(3)    let p = new Promise((resolve, reject) => {      console.log(7)      setTimeout(() => {        console.log(5)        resolve(6)      }, 0)      resolve(1)    })    resolve(2)    p.then((arg) => {      console.log(arg)    })  })first().then((arg) => {  console.log(arg)})console.log(4)

二:红灯三秒亮一次,绿灯一秒亮一次,黄灯 2 秒亮一次;如何让三个灯一直交替反复亮灯?(用 Promise 实现)三个亮灯函数曾经存在:

function red() {  console.log('red')}function green() {  console.log('green')}function yellow() {  console.log('yellow')}

三:实现 mergePromise 函数,把传进去的数组按程序先后执行,并且把返回的数据先后放到数组 data 中。

const timeout = (ms) =>  new Promise((resolve, reject) => {    setTimeout(() => {      resolve()    }, ms)  })const ajax1 = () =>  timeout(2000).then(() => {    console.log('1')    return 1  })const ajax2 = () =>  timeout(1000).then(() => {    console.log('2')    return 2  })const ajax3 = () =>  timeout(2000).then(() => {    console.log('3')    return 3  })const mergePromise = (ajaxArray) => {  // 在这里实现你的代码}mergePromise([ajax1, ajax2, ajax3]).then((data) => {  console.log('done')  console.log(data) // data 为 [1, 2, 3]})// 要求别离输入// 1// 2// 3// done// [1, 2, 3]

题目解析

一、

Answer:

// 运行后果:// => 3// => 7// => 4// => 1// => 2// => 5

这道题次要了解 js 执行机制。

第一轮事件循环,先执行宏工作,主 script,new Promise 立刻执行,输入 3,执行 p 这个 new Promise 操作,输入 7,发现 setTimeout ,将回调函数放入下一轮工作队列(Event Quene),p 的 then ,暂且命名为 then1,放入微工作队列,且 first也有 then,命名为 then2,放入微工作队列。执行 console.log(4) ,输入 4,宏工作执行完结。

再执行微工作,执行 then1 ,输入 1,执行 then2 ,输入 3.

第一轮事件循环完结,开始执行第二轮。第二轮事件循环先执行宏工作外面的,也就是 setTimeout 的回调,输入 5.resolve(6) 不会失效,因为 p 的 Promise 状态一旦扭转就不会再变动了。

二、

Answer:

var light = function (timmer, cb) {  return new Promise(function (resolve, reject) {    setTimeout(function () {      cb()      resolve()    }, timmer)  })}var step = function () {  Promise.resolve()    .then(function () {      return light(3000, red)    })    .then(function () {      return light(2000, green)    })    .then(function () {      return light(1000, yellow)    })    .then(function () {      step()    })}step()

红灯三秒亮一次,绿灯一秒亮一次,黄灯 2 秒亮一次,意思就是 3 秒,执行一次 red 函数,2 秒执行一次 green 函数,1 秒执行一次 yellow 函数,一直交替反复亮灯,意思就是依照这个程序始终执行这 3 个函数,这步能够就利用递归来实现。

当然,采纳其余办法能实现所示成果,办法很多,这边只提供一种形式。

三、

Answer:

// 保留数组中的函数执行后的后果var data = []// Promise.resolve办法调用时不带参数,间接返回一个resolved状态的 Promise 对象。var sequence = Promise.resolve()ajaxArray.forEach(function (item) {  // 第一次的 then 办法用来执行数组中的每个函数,  // 第二次的 then 办法承受数组中的函数执行后返回的后果,  // 并把后果增加到 data 中,而后把 data 返回。  // 这里对 sequence 的从新赋值,其实是相当于缩短了 Promise 链  sequence = sequence.then(item).then(function (res) {    data.push(res)    return data  })})// 遍历完结后,返回一个 Promise,也就是 sequence, 他的 [[PromiseValue]] 值就是 data,// 而 data(保留数组中的函数执行后的后果) 也会作为参数,传入下次调用的 then 办法中。return sequence

首先 ajax1ajax2ajax3 都是函数,只是这些函数执行后会返回一个 Promise,按题目的要求咱们只有程序执行这三个函数就好了,而后把后果放到 data 中,然而这些函数里都是异步操作,想要按程序执行,而后输入 1,2,3 并没有那么简略,看个例子。

function A() {  setTimeout(function () {    console.log('a')  }, 3000)}function B() {  setTimeout(function () {    console.log('b')  }, 1000)}A()B()// b// a

例子中咱们是按程序执行的 A,B 然而输入的后果却是 b,a 对于这些异步函数来说,并不会按程序执行完一个,再执行后一个。这道题就是考用 Promise 管制异步流程,咱们要想方法,让这些函数,一个执行完之后,再执行下一个。