摘要:源码学习本篇为上一篇源码学习的补充,主要是来介绍和方法。那个率先改变的实例的返回值,就传递给的回调函数。基本介绍可见阮一峰老师的书籍。的状态由决定,分成两种情况。只有的状态都变成,的状态才会变成,此时的返回值组成一个数组,传递给的回调函数。
Promise源码学习(2)
本篇为上一篇源码学习(1)的补充,主要是来介绍Promise.all()和Promise.race()方法。
闲话少叙,进入正题
首先来简单介绍一下功能吧,详细比如可见阮一峰老师的ES6书籍。
Promise.race方法是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
接下来贴源代码
export default function race (entries) { /*jshint validthis:true */ let Constructor = this; if (!isArray(entries)) { return new Constructor((_resolve, reject) => reject(new TypeError("You must pass an array to race."))); } else { return new Constructor((resolve, reject) => {//new 执行一次 let length = entries.length; for (let i = 0; i < length; i++) {//执行每一个传入的entry 但只有最快的一个能resolve或reject改变返回的promise的状态 Constructor.resolve(entries[i]).then(resolve, reject); } }); } } //isArray定义: if (Array.isArray) { _isArray = Array.isArray; } else { _isArray = x => Object.prototype.toString.call(x) === "[object Array]"; }
如果传入的参数不是数据直接reject。
如果是数组,则依次resolve传入的thenable对象并在then中注册回调,设Promise.race()返回的新Promise的对象为p的话,最快执行完成的entry进入then回调,执行resolve或reject,以此来改变新对象p的状态。之后的entry完成在此执行resolve或reject均无效,因为Promise状态一旦确定无法改变,详见上篇关于fulfill()和reject()的注释和分析。
基本介绍可见阮一峰老师的ES6书籍。
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
p的状态由p1、p2、p3决定,分成两种情况。
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
源码:
export default function all(entries) { return new Enumerator(this, entries).promise;//这里返回了一个新对象Promise }
这里注意返回了一个新的Promise对象.
Enumerator源码如下
export default class Enumerator { constructor (Constructor, input) { this._instanceConstructor = Constructor; //Promise.all([...])会返回一个新的promise this.promise = new Constructor(noop); if (!this.promise[PROMISE_ID]) { makePromise(this.promise); } if (isArray(input)) { this.length = input.length; this._remaining = input.length;//未完成的promise总数量 this._result = new Array(this.length);//每个promise结果 if (this.length === 0) { fulfill(this.promise, this._result); } else { this.length = this.length || 0; this._enumerate(input);//处理输入的数组 if (this._remaining === 0) {//都执行完毕 fulfill(this.promise, this._result); } } } else { reject(this.promise, validationError());//传入不是array, reject it } } _enumerate (input) { for (let i = 0; this._state === PENDING && i < input.length; i++) {//Enumerator _state?? TODO this._eachEntry(input[i], i); } } //处理所有的输入 _eachEntry (entry, i) { let c = this._instanceConstructor;//Promise let {resolve} = c;//Promise.resolve if (resolve === originalResolve) { let then = getThen(entry);//获取then方法 if (then === originalThen && entry._state !== PENDING) {//如果entry已完成或已拒绝 this._settledAt(entry._state, i, entry._result); } else if (typeof then !== "function") { this._remaining--;//不是thenable 直接完成该entry this._result[i] = entry; } else if (c === Promise) {//不是promise但是一个thenable let promise = new c(noop); handleMaybeThenable(promise, entry, then); this._willSettleAt(promise, i);//暂时状态不确定,订阅之 } else { this._willSettleAt(new c(resolve => resolve(entry)), i); } } else { this._willSettleAt(resolve(entry), i); } } _settledAt (state, i, value) { let {promise} = this; if (promise._state === PENDING) { this._remaining--;//该entry状态已确定,待完成总数减一 if (state === REJECTED) { reject(promise, value);//如果传入entry列表有一个rejected,立即设置promise结果rejected } else { this._result[i] = value; } } if (this._remaining === 0) {//全部处理完成fulfill fulfill(promise, this._result); } } _willSettleAt (promise, i) { let enumerator = this; //暂时状态不定,订阅之 subscribe( promise, undefined, value => enumerator._settledAt(FULFILLED, i, value),//回调,设置promise状态 reason => enumerator._settledAt(REJECTED, i, reason) ); } };
代码不是很多,在此就不逐个方法贴了。
首先看Constructor,细节不表,如果传入了一个thenable数组会在_enumerate方法中通过_eachEntry挨个处理,细节见注释。
总体思路就是对传入列表的元素挨个处理,该resolve则resolve,同时通过_remaining 对未完成的entry进行计数。
若entry是pending状态,则通过_willSettleAt来订阅,有确定结果时进行 _settledAt;
若entry已完成,直接_settledAt确定结果;
当_remaining === 0;也就是列表所有entry均已有结果,设置Promise.all()返回的新Promise对象的状态。
要注意,如果有一个entry被reject了,会直接设置 新Promise对象的状态为rejected。
该图对Promise的流程总结。
总的来说,Promise通过链式语法使得异步操作更加的直观,避免了回调地狱的出现。使得代码更加易读可维护。
细节可见源码上的注释,全部代码可见es6-promise学习笔记
明后两天公司集体出游,没有大多的时间来打磨,可是自己又定了一个每周一篇学习总结小文章的目标,所以挤些时间提前写完这篇文章来完成目标吧。继续加油吧!
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/94468.html
摘要:工作当中经常会用到,在此进行深入学习异步编程解决方案是异步编程的一种解决方案,比传统的解决方案回调函数和事件更合理和更强大。所有源码注释见学习笔记 工作当中经常会用到Promise,在此进行深入学习 异步编程解决方案 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了...
摘要:源码阅读阶段紧接上一篇这次我们开始我们最常用到的部分的源码解析传入参数为两个函数和判断调用者是否为对象跳转到了一个叫做的函数里面新建一个对象传入函数传入给和一个新的对象返回新的对象在这里我们先看看在调用者不是对象时到底做了什么比想象的要简单 源码阅读阶段 紧接上一篇,这次我们开始Promise我们最常用到的then部分的源码解析. then() //传入参数为两个函数,onFulfil...
摘要:源码阅读阶段先理解根本吧想快点理解的话可以直接跳到下个标题这部分根据理解将持续修改空函数用于判断传入构造器的函数是否为空函数如果为空函数构造一个对象并初始化状态为终值回调状态和队列记录内部最后的一次错误空对象标识表示发生了错误暴露模块接口为 源码阅读阶段 先理解Promise根本吧,想快点理解的话可以直接跳到下个标题.这部分根据理解将持续修改. Promise(fn) function...
摘要:第三篇脚手架依赖的核心库的源码解析。该篇是这个系列文章的第三篇主要是对的源码进行分析讲解。的源码十分简单但实现的功能却是十分的强大。源码概括源码主要包含了两部分公共方法和私有方法。 react作为当前十分流行的前端框架,相信很多前端er都有蠢蠢欲动的学习它的想法。工欲善其事,必先利其器。这篇文章就简单的给大家介绍一下如何我快速的搭建一个react前端开发环境。主要针对于react小白,...
摘要:下一篇大概就是源码方面的学习笔记了龟速学习中这一次我是去看了下规范照例传送门图灵社区规范首先吧个人总结下该用的词解决结婚拒绝婉拒终值值传家宝拒因好人卡等等异常车祸理下概念我们的的就像是一场姻缘对吧解决呢就是结婚成功啦传家宝也如愿的传给下一代 下一篇大概就是源码方面的学习笔记了...龟速学习中... 这一次我是去看了下Promises/A+规范照例传送门:图灵社区Promises/A+规...
阅读 1994·2021-11-24 10:45
阅读 1851·2021-10-09 09:43
阅读 1292·2021-09-22 15:38
阅读 1219·2021-08-18 10:19
阅读 2839·2019-08-30 15:55
阅读 3059·2019-08-30 12:45
阅读 2963·2019-08-30 11:25
阅读 358·2019-08-29 11:30