热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Promise源码剖析

媒介thenpromise项目是基于PromisesA+规范完成的Promise库,从这个项目当中,我们来看Promise的道理是什么,它是怎样做到的,从而越发熟习Promise剖

媒介

then/promise项目是基于Promises/A+规范完成的Promise库,从这个项目当中,我们来看Promise的道理是什么,它是怎样做到的,从而越发熟习Promise

剖析

从index.js当中晓得,它是先引出了./core.js,随后各自实行了其他文件的代码,经由过程requeire的要领。

我们起首先想一下最基本的promise用法

new Promise((resolve, reject) => {
resolve(4);
}).then(res => {
console.log(res); // export 4
});

Promise中的规范

规范中划定:

  1. Promise对象初始状况为 Pending,在被 resolvereject 时,状况变成 FulfilledRejected
  2. resolve吸收胜利的数据,reject吸收失利或毛病的数据
  3. Promise对象必须有一个 then 要领,且只接收两个可函数参数 onFulfilledonRejected

index.js

'use strict';
module.exports = require('./core.js');
require('./done.js');
require('./finally.js');
require('./es6-extensions.js');
require('./node-extensions.js');
require('./synchronous.js');

我们先看src/core.js

function Promise(fn) {
// 推断 this肯定得是object不然就会报错,这个要领肯定得要new出来
if (typeof this !== 'object') {
throw new TypeError('Promises must be constructed via new');
}
// 推断fn 肯定得是一个函数
if (typeof fn !== 'function') {
throw new TypeError('Promise constructor\'s argument is not a function');
}
this._deferredState = 0;
this._state = 0;
this._value = null;
this._deferreds = null;
if (fn === noop) return;
// 终究doResolve很症结
doResolve(fn, this);
}

Promise是一个组织要领,开始时,它举行了校验,确保了fn是一个函数,随后对一些变量举行了初始化,末了实行了doResolve()

我们接着看doResolve这个要领。

/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/
//
// 确保`onFulfilled`和`onRejected`要领只挪用一次
// 不保证异步
function doResolve(fn, promise) {
var dOne= false;
var res = tryCallTwo(fn, function (value) {
// 假如done 为true 则return
if (done) return;
dOne= true;
// 回调实行 resolve()
resolve(promise, value);
}, function (reason) {
// 假如done 为true 则return
if (done) return;
dOne= true;
reject(promise, reason);
});
// res为truCallTwo()的返回值
// 假如done没有完成 而且 res 是 `IS_ERROR`的情况下
// 也会实行reject(),同时让done完成
if (!done && res === IS_ERROR) {
dOne= true;
reject(promise, LAST_ERROR);
}
}

doResolve最症结的是实行了tryCallTwo要领,这个要领的第二,第三个参数都是回调,当实行回调后,done为true,同时各自会实行resolve()或许reject()要领。末了当tryCallTwo的返回值为IS_ERROR时,也会实行reject()要领。

我们先来看一下tryCallTwo要领

function tryCallTwo(fn, a, b) {
try {
fn(a, b);
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}

fn现实就是Promise初始化时的匿名函数(resolve, reject) => {}ab则代表的是resolve()reject()要领,当我们平常实行完promise函数时,则实行的是resolve则在doResolve中,我们当时实行的第二个参数被回调,假如报错,reject()被实行,则第二个参数被回调。末了捕捉了非常,当发生了报错时,会return IS_ERROR,非报错时会return undinfed

再回到适才的doResolve要领,当实行了第二个参数的回调以后,会实行resolve要领

function resolve(self, newValue) {
// Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
// 不能吃通报自身
if (newValue === self) {
// 报错
return reject(
self,
new TypeError('A promise cannot be resolved with itself.')
);
}
// promise作为参数
if (
newValue &&
(typeof newValue === 'object' || typeof newValue === 'function')
) {
// 猎取它的promise要领 读取newValue.then
var then = getThen(newValue);
if (then === IS_ERROR) {
// 假如then IS_ERROR
return reject(self, LAST_ERROR);
}
if (
// 假如then是self的then
// 而且Promise
then === self.then &&
// newValue 属于Promise
newValue instanceof Promise
) {
// _state为3
// 平常then以后走这里
// 实行then(newValue)返回了promise
self._state = 3;
// selft.value为newValue
self._value = newValue;
// 当state为3时实行 finale
finale(self);
return;
} else if (typeof then === 'function') {
doResolve(then.bind(newValue), self);
return;
}
}
self._state = 1;
self._value = newValue;
finale(self);
}

在没有链式挪用then的情况下(也就是只需一个then)的情况下,会将内部状况_state设置成3,将传入值赋给内部变量_value末了会实行final()要领,不但是会运用doResolve来挪用then

我们再来看下reject

function reject(self, newValue) {
// _state = 2为reject
self._state = 2;
self._value = newValue;
if (Promise._onReject) {
Promise._onReject(self, newValue);
}
finale(self);
}

reject当中我们的_state变动为了2,一样末了finale被挪用。

我们来看下finale函数

// 实行自身的deferreds
function finale(self) {
if (self._deferredState === 1) {
handle(self, self._deferreds);
self._deferreds = null;
}
if (self._deferredState === 2) {
for (var i = 0; i // 遍历handle
handle(self, self._deferreds[i]);
}
// 将deferred 置空
self._deferreds = null;
}
}

在该要领当中依据差别的_deferredState,会实行差别的handle要领。

我们再来看handle要领

function handle(self, deferred) {
while (self._state === 3) {
self = self._value;
}
// 假如有onHandle要领 则实行该要领
if (Promise._onHandle) {
Promise._onHandle(self);
}
// (初始 _state 为0)
if (self._state === 0) {
// (初始 _deferredState 为0)
if (self._deferredState === 0) {
self._deferredState = 1;
self._deferreds = deferred;
return;
}
// 假如 _deferredState是1 则__deferreds是一个数组
if (self._deferredState === 1) {
self._deferredState = 2;
self._deferreds = [self._deferreds, deferred];
return;
}
// 当走到这里 _deferredState应当是2 将deferred
// 插进去到数组当中
self._deferreds.push(deferred);
return;
}
handleResolved(self, deferred);
}

这里比较症结的应当就是经由过程deferredState差别的状况,将deferred放入deferreds当中。别的当我们的_state不为0时,终究会实行handleResolved

继承看handleResolve()要领

function handleResolved(self, deferred) {
asap(function() {
// _state为1时,cb = onFulfilled 不然 cb = onRejected
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
if (self._state === 1) {
resolve(deferred.promise, self._value);
} else {
reject(deferred.promise, self._value);
}
return;
}
var ret = tryCallOne(cb, self._value);
if (ret === IS_ERROR) {
reject(deferred.promise, LAST_ERROR);
} else {
resolve(deferred.promise, ret);
}
});
}.then((res) => {
}).catch((error) => {
})

在这个要领当中,会依据我们使命(_state)的差别状况,来实行onFulfilled或许onRejected要领。当此要领挪用时,也就是我们一个简朴的Promise的完毕。

回到适才说的Promise组织要领完毕的时刻

设置了Promise函数的一些变量

Promise._OnHandle= null;
Promise._OnReject= null;
Promise._noop = noop;

随后在Promise的原型上设置了then要领。

Promise.prototype.then = function(onFulfilled, onRejected) {
// 起首看这是谁组织的 假如不是promise
// 则return 实行safeThen
if (this.constructor !== Promise) {
return safeThen(this, onFulfilled, onRejected);
}
// 假如是则初始化一个Promise 但是参数 noop 为空对象 {}
var res = new Promise(noop);
// 随后实行handle要领
handle(this, new Handler(onFulfilled, onRejected, res));
return res;
};

then这个要领中起首推断了它是不是由Promise组织的,假如不是,则返回并实行safeThen,不但是实行Promise组织一个res对象,然后实行handle要领,末了将promise变量res返回。handle要领之前有提过,在这里,当初始化时_state_deferred的转改都为0,因而它会将defrred保留到promise当中。

先看一下上面说的safeThen要领

function safeThen(self, onFulfilled, onRejected) {
return new self.constructor(function (resolve, reject) {
var res = new Promise(noop);
res.then(resolve, reject);
handle(self, new Handler(onFulfilled, onRejected, res));
});
}

流程

须要有一个Promise的组织要领,这个组织要领终究会实行它的参数(resolve, reject) => {},声明的then要领会经由过程handle()要领将onFulfilledonRejected要领保留起来。当在外部挪用resolve或许onRejected时,终究也会实行handle但是它,会末了依据状况来实行onFulfilled或许onRejected。从而到我们的then回调中。

Promise的扩大

done

done的扩大在src/done.js当中

'use strict';
var Promise = require('./core.js');
module.exports = Promise;
Promise.prototype.dOne= function (onFulfilled, onRejected) {
var self = arguments.length ? this.then.apply(this, arguments) : this;
self.then(null, function (err) {
setTimeout(function () {
throw err;
}, 0);
});
};

内部实行了then()

finally

finally的扩大在src/finally.js当中

Promise的规范当中,自身是没有finally要领的,但是在ES2018的规范里有,finally的完成以下

'use strict';
var Promise = require('./core.js');
module.exports = Promise;
Promise.prototype.finally = function (callback) {
return this.then(function (value) {
return Promise.resolve(callback()).then(function () {
return value;
});
}, function (err) {
return Promise.resolve(callback()).then(function () {
throw err;
});
});
};

PromiseonFulfilledonRejected 不论回调的哪一个,终究都邑触发callback 回调。还要注重的一点是finally的返回也是一个Promise

es6-extensions.js

es6-extensions.js文件当中包含了ES6的一些扩大。

Promise.resolve

function valuePromise(value) {
var p = new Promise(Promise._noop);
// 将_state赋值为 非0
// _value举行保留
p._state = 1;
p._value = value;
// 如许做的目标是省略的一些前面的逻辑
return p;
}
Promise.resolve = function (value) {
if (value instanceof Promise) return value;
if (value === null) return NULL;
if (value === undefined) return UNDEFINED;
if (value === true) return TRUE;
if (value === false) return FALSE;
if (value === 0) return ZERO;
if (value === '') return EMPTYSTRING;
// value return new Promise
if (typeof value === 'object' || typeof value === 'function') {
try {
var then = value.then;
if (typeof then === 'function') {
// 返回 返回了一个新的Promise对象
return new Promise(then.bind(value));
}
} catch (ex) {
// 假如报错 则返回一个就只
return new Promise(function (resolve, reject) {
reject(ex);
});
}
}
return valuePromise(value);
};

Promise.reject

Promise.reject = function (value) {
return new Promise(function (resolve, reject) {
reject(value);
});
};

Promise.all

Promise.all = function (arr) {
// 相似深拷贝了一份给了args
var args = Array.prototype.slice.call(arr);
return new Promise(function (resolve, reject) {
// 推断了all的promise数目
if (args.length === 0) return resolve([]);
// remaining则是promise数组的长度
var remaining = args.length;
// i为index val 为 promise
function res(i, val) {
if (val && (typeof val === 'object' || typeof val === 'function')) {
if (val instanceof Promise && val.then === Promise.prototype.then) {
while (val._state === 3) {
val = val._value;
}
if (val._state === 1) return res(i, val._value);
if (val._state === 2) reject(val._value);
// val._state 为 0时 走这里
val.then(function (val) {
res(i, val);
}, reject);
return;
} else {
var then = val.then;
if (typeof then === 'function') {
var p = new Promise(then.bind(val));
p.then(function (val) {
res(i, val);
}, reject);
return;
}
}
}
args[i] = val;
// 当一切的promise实行完 则是remaining为0
// 则实行resolve();
if (--remaining === 0) {
resolve(args);
}
}
// 遍历一切的promise
for (var i = 0; i res(i, args[i]);
}
});
};

Promise.all()返回的也是一个Promise函数。
内部有一个remaining变量每当实行完一个promise函数后就会减一,当一切promise实行完,会实行自身的resolve

Promise.race

Promise.race = function (values) {
return new Promise(function (resolve, reject) {
values.forEach(function(value){
Promise.resolve(value).then(resolve, reject);
});
});
};

遍历传入的promise数组,经由Promise.resolve(value)的源码能够看到,假如value是一个Promise则户直接将这个value返回,末了数组中的promise哪一个优先回调即实行。

Promise.property.catch

catch在规范当中也是没有,虽然我们用的比较多

Promise.prototype['catch'] = function (onRejected) {
return this.then(null, onRejected);
};

catch的回调现实是then(null, onRejected)的回调。

广而告之

本文宣布于薄荷前端周刊,迎接Watch & Star ★,转载请说明出处。

迎接议论,点个赞再走吧 。◕‿◕。 ~


推荐阅读
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文详细解析了JavaScript中相称性推断的知识点,包括严厉相称和宽松相称的区别,以及范例转换的规则。针对不同类型的范例值,如差别范例值、统一类的原始范例值和统一类的复合范例值,都给出了具体的比较方法。对于宽松相称的情况,也解释了原始范例值和对象之间的比较规则。通过本文的学习,读者可以更好地理解JavaScript中相称性推断的概念和应用。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
author-avatar
少才奇妙Albert
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有