重新认识Promise(二)

359 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第27天,点击查看活动详情

🎉前言

Promise 不管是日常开发还是面试中,都是大家经常碰到的,所以掌握它是很有必要的,了解它的原理就能在遇到问题时能够更快的定位哦。

相信大家有了第一章节的基础,对 Promise 也有了一定的了解,这一期文章就带大家将剩下的功能实现吧。

🎊 ​实现前准备

我们把在第一章节 延迟调用 的代码实现稍微改一下:

// 初始化值
initValue () {
    //...省略代码
​
    // 把回调结果保存起来
+    this.onFulfilledList = [];
+    this.onRejectedList = [];
}
​
​
resolve (value) {
    //...省略代码
​
+    // 执行保存的函数
+    this.onFulfilledList.shift()(this.PromiseResult);
}
​
reject (reson) {
     //...省略代码
​
+    // 执行保存的函数
+    this.onRejectedList.shift()(this.PromiseResult);
}
​
then (onFulfilled, onRejected) {
    //...省略代码
​
    // 如果状态为 fulfilled 就执行 onFulfilled
    if (this.PromiseState === 'fulfilled') {
        onFulfilled(this.PromiseResult);
​
        // 否则如果状态为 rejected 就执行 onRejected
    } else if (this.onRejected === 'rejected') {
        onRejected(this.PromiseResult);
+    } else {
+        // 否则 就是 pending 状态,保存两个回调函数
+        this.onFulfilledList.push(onFulfilled);
+        this.onRejectedList.push(onFulfilled);
+    }
}

测试一下:

let test5 = new MyPromise((resolve,reject) => {
    setTimeout(() => {
    resolve('成功啦,兄嘚!!')
    }, 1000);
}).then(res => {
console.log('res:', res); // 成功啦,兄嘚!!
}, err => {
console.log(err);
})

🍻 ​链式调用

Promise.then 方法支持 链式调用,下一次 then 的执行结果受上一次 then的返回值的影响,例子如下:

const test1 = new Promise((resolve, reject) => {
    resolve(100) // 打印 200
}).then(res => {
    return new Promise((resolve,reject) => {
        resolve(2 * res)
    })
},err => {
    console.log(err);
}).then(res => {
    console.log(res);
}, err => {
    console.log(err);
})

上述结果可以总结出四个知识:

  1. then 方法本身会返回一个新的 Promise 对象。
  2. 如果返回值是 Promise 对象,返回值为成功,新 Promise 就是成功。
  3. 如果返回值是 Promise 对象,返回值为失败,新 Promise 就是失败。
  4. 如果返回值非 Promise 对象,新 Promise 对象就是成功,值为此返回值。

值得一提的是,第2点和第3点可以简单一句话概况:Promise 的最终状态以最后一次为准。

那如何实现 then 完还能再 then 呢?答案是,then 完一次后在返回一次 Promise 对象就行了,有点像套娃的感觉。

具体实现(修改 then 里的逻辑):

then (onFulfilled, onRejected) {
    // 校验两个参数是否是函数,这里使用 typeof
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
    onRejected = typeof onRejected === 'function' ? onRejected : reson => { throw reson };
​
+    var thenPromise = new MyPromise((resolve,reject) => {
+        const resultPromise = cb => {
+            try {
+                //执行第一个(当前的)Promise的成功回调,并获取返回值
+                const x = cb(this.PromiseResult);
​
+                //如果是Promise,那么等待Promise状态变更,否则直接resolve
+                //这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用
+                x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
+            } catch (e) {
+                // 处理报错
+                reject(err);
+                throw new Error(err);
+            }
+        }
​
        // 如果状态为 fulfilled 就执行 onFulfilled
        if (this.PromiseState === 'fulfilled') {
+            resultPromise(onFulfilled);
​
            // 否则如果状态为 rejected 就执行 onRejected
        } else if (this.onRejected === 'rejected') {
+            resultPromise(onRejected);
        } else {
            // 否则 就是 pending 状态,保存两个回调函数
+            this.onFulfilledList.push(resultPromise.bind(this, onFulfilled));
+            this.onRejectedList.push(resultPromise.bind(this, onRejected));
        }
    })
​
    // 返回这个包装的Promise
    return thenPromise;
}

测试代码:

const test2 = new MyPromise((resolve, reject) => {
    resolve(100)   // 打印:  成功==> 200
}).then(res => {
    return new MyPromise((resolve,reject) => {
        resolve(2 * res)
    })
},err => {
    return new MyPromise((resolve,reject) => {
        resolve(3 * err)
    })
}).then(res => {
    console.log('成功==>', res)
}, err => {
    console.log('失败==>', err)
})

♠️ ​Promise.all

Promise.all 的返回值是一个新的 Promise 实例。

Promise.all 接受一个可遍历的数据容器,容器中每个元素都应该是 Promise 的实例。假设这个容器是一个数组。

只有当数组中的每个 Promise 实例都成功时 Promise.all 才会返回成功。这些 Promise 实例所有的 resolve 结果会按照原来的顺序集合在一个数组中作为 Promise.allresolve 的结果。

数组中只要有一个 Promise 实例失败,那么 Promise.all 就会失败。Promise.all.catch() 会捕获到这个 reject

原生 Promise.all 的效果:

const test1 = Promise.resolve('test1')
​
const test2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('test2 延时一秒')
  }, 1000)
})
​
const test3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('test3 延时两秒')
  }, 2000)
})
​
const test4 = Promise.reject('test4 rejected')
​
const test5 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('test5 rejected 延时1.5秒')
  }, 1500)
})
​
// 所有Promise实例都成功
Promise.all([test1, test2, test3])
  .then(res => {
    console.log(res)
  })
  .catch(err => console.log(err)) // 2秒后打印 [ 'test1', 'test2 延时一秒', 'test3 延时两秒' ]// 一个Promise实例失败
Promise.all([test1, test2, test4])
  .then(res => {
    console.log(res)
  })
  .catch(err => console.log(err)) // test4 rejected// 一个延时失败的Promise
 Promise.all([test1, test2, test5])
  .then(res => {
    console.log(res)
  })
  .catch(err => console.log(err)) // 1.5秒后打印 test5 rejected// 两个Promise实例失败
Promise.all([test1, test4, test5])
  .then(res => {
    console.log(res)
  })
  .catch(err => console.log(err)) // test4 rejected

这里要注意的是:

  • 如果有两个Promise实例失败了,则返回最后一个。
  • 如果有异步的情况,例如 setTimeout ,则返回结果不定。

🎼 ​手动实现

Promise.MyAll 接收一个数组,返回一个新的 Promise 实例。

Promise.MyAll = function (promises) {
  let arr = [], count = 0;
  return new Promise((resolve, reject) => {
    promises.forEach((item, index) => {
      Promise.resolve(item).then(res => {
        arr[index] = res;
        // 成功后 +1  
        count += 1;
          
        // 判断是否相等
        if (count === promises.length) resolve(arr);
      }, reject)
    })
  })
}

我们需要一个数组来收集这些 Promise 实例的 resolve 结果。需要一个计算器,记录Promise 实例的成功,成功后,计算器 +1。计算结束后,如果计算总数等于 promises 的个数,就执行 resolve,否则就是失败,执行 reject

♥️ ​Promise.any

Promise.anyPromise.all 可以看做是相反的。Promise.any 中只要有一个 Promise 实例成功就成功,只有当所有的 Promise 实例失败时 Promise.any 才失败。

🎼 ​手动实现

Promise.MyAny = function (promises) {
  let arr = [],count = 0;
  return new Promise((resolve, reject) => {
    promises.forEach((item, index) => {
      Promise.resolve(item).then(resolve, err => {
        arr[i] = { status: 'rejected', val: err };
        count += 1;
        if (count === promises.length) reject(new Error('no promise succeeded'));
      })
    })
  })
}

这里跟 Promise.all 类似,也需要一个数组和计数器,用计数器判断是否所有的 Promise 实例都失败。

♣️ ​Promise.race

Promise 的赛跑模式,以状态变化最快的那个 Promise 实例为准,最快的 Promise 成功 Promise.race 就成功,最快的 Promise 失败 Promise.race 就失败。

🎼 ​手动实现

Promise.MyRace = function (promises) {
  return new Promise((resolve, reject) => {
    for (const item of promises) {
      Promise.resolve(item).then(resolve, reject)
    }
  })
}

♦️ ​Promise.allSettled

Promise.allSettled()方法返回一个在所有给定的 Promise 都已经 fulfilledrejected 后的Promise,并带有一个对象数组,每个对象表示对应的Promise结果。

🎼 ​手动实现

我们用个数组把所有的 Promise 实例的结果(无论成功与否)都收集起来,判断收集完了(所有 Promise 实例状态都改变了),咱就将这个收集到的结果 resolve 掉。收集成功 Promise 结果的逻辑咱们在 Promise.all 中实现过,收集失败 Promise 结果咱们在 Promise.any 中处理过。

Promise.MyAllSettled = function (promises) {
  let arr = [],count = 0;
  return new Promise((resolve, reject) => {
    promises.forEach((item, index) => {
      Promise.resolve(item).then(res => {
        arr[i] = { status: 'fulfilled', val: res };
        count += 1;
        if (count === promises.length) resolve(arr);
      }, (err) => {
        arr[i] = { status: 'rejected', val: err };
        count += 1
        if (count === promises.length) resolve(arr);
      })
    })
  })
}

上述的代码跟 Promise.any 也有些类似,就是加了对 fulfilled 的收集而已。

✨总结

以上就是本次分享的全部内容~~

如果觉得文章写得不错,对你有所启发的,请不要吝啬 点个 关注 并在 评论区 留下你宝贵的意见哦~~😃