javascript 作用_JavaScript承诺如何从内到外真正发挥作用

javascript 作用

One of the most important questions I faced in interviews was how promises are implemented. Since async/await is becoming more popular, you need to understand promises.

我在采访中面临的最重要的问题之一是如何实现承诺。 由于异步/等待变得越来越流行,因此您需要了解Promise。

什么是诺言? (What is a Promise?)

A promise is an object which represents the result of an asynchronous operation which is either resolved or rejected (with a reason).

一个promise是一个对象,它表示异步操作的结果,该结果被解决或被拒绝(有原因)。

There are 3 states

有3个州

  • Fulfilled: onFulfilled() will be called (e.g., resolve() was called)

    已实现:将调用onFulfilled() (例如,调用了resolve() )

  • Rejected: onRejected() will be called (e.g., reject() was called)

    拒绝:将调用onRejected() (例如,调用了reject() )

  • Pending: not yet fulfilled or rejected

    待处理:尚未实现或拒绝

So let’s see how’s it is implemented:

因此,让我们看看它是如何实现的:

https://github.com/then/promise/blob/master/src/core.js

https://github.com/then/promise/blob/master/src/core.js

According to the definition at Mozilla: It takes an executor function as an argument.

根据Mozilla的定义:它以执行程序函数作为参数。

function noop() {} function Promise(executor) {if (typeof this !== 'object') {throw new TypeError('Promises must be constructed via new');}if (typeof executor !== '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 (executor === noop) return;doResolve(executor, this);
}

Looks like a simple function with some properties initialized to 0 or null. Here are a few things to notice:

看起来像一个简单的函数,其某些属性初始化为0null 。 这里有一些注意事项:

this._state property can have three possible values as described above:

this._state 属性可以具有三个如上所述的可能值:

0 - pending1 - fulfilled with _value2 - rejected with _value3 - adopted the state of another promise, _value

Its value is0 (pending) when you create a new promise.

创建新的承诺时,其值为0 ( 待定)

Later doResolve(executor, this) is invoked with executor and promise object.

之后, doResolve(executor, this)executor and promise对象一起调用。

Let’s move on to the definition of doResolve and see how it’s implemented.

让我们继续进行doResolve的定义,看看它是如何实现的。

/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/function doResolve(fn, promise) {var done = false;var resolveCallback = function(value) {if (done) return;done = true;resolve(promise, value);};var rejectCallback = function(reason) {if (done) return;done = true;reject(promise, reason);
};var res = tryCallTwo(fn, resolveCallback, rejectCallback);if (!done && res === IS_ERROR) {done = true;reject(promise, LAST_ERROR);}
}

Here it is again calling tryCallTwo function with executor and 2 callbacks. The callbacks are again calling resolve and reject

在这里,它再次使用executor和2个回调调用tryCallTwo函数。 回调再次调用resolvereject

The done variable is used here to make sure the promise is resolved or rejected only once, so if you try to reject or resolve a promise more than once then it will return because done = true.

这里, done变量用于确保仅对诺言进行一次解析或拒绝,因此,如果您多次尝试拒绝或解决诺言,则它将返回,因为done = true

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

This function indirectly calls the main executor callback with 2 arguments. These arguments contain logic on how resolve or reject should be called. You can check resolveCallback and rejectCallback in doResolve function above.

此函数使用2个参数间接调用主executor回调。 这些参数包含有关如何调用resolvereject逻辑。 您可以在上面的doResolve函数中检查resolveCallbackrejectCallback

If there is an error during execution it will store the error in LAST_ERROR and return the error.

如果执行期间发生错误,它将错误存储在LAST_ERROR并返回错误。

Before we jump to the resolve function definition, let’s check out the .then function first:

在跳转到resolve函数定义之前,让我们先检查.then函数:

Promise.prototype.then = function(onFulfilled, onRejected) {if (this.constructor !== Promise) {return safeThen(this, onFulfilled, onRejected);}var res = new Promise(noop);handle(this, new Handler(onFulfilled, onRejected, res));return res;
};function Handler(onFulfilled, onRejected, promise) {this.onFulfilled = typeof onFulfilled === "function" ? onFulfilled  : null;this.onRejected = typeof onRejected === "function" ? onRejected :  null;this.promise = promise;
}

So in the above function, then is creating new promise and assigning it as a property to a new function called Handler. The Handler function has arguments onFulfilled and onRejected. Later it will use this promise to resolve or reject with value/reason.

因此,在上述函数中,将创建新的promise并将其作为属性分配给一个名为Handler的新函数。 Handler函数具有onFulfilledonRejected参数 稍后,它将使用此承诺以价值/理由来解决或拒绝。

As you can see, the .then function is calling again another function:

如您所见, .then函数再次调用另一个函数:

handle(this, new Handler(onFulfilled, onRejected, res));

实现方式: (Implementation:)

function handle(self, deferred) {while (self._state === 3) {self = self._value;}if (Promise._onHandle) {Promise._onHandle(self);}if (self._state === 0) {if (self._deferredState === 0) {self._deferredState = 1;self._deferreds = deferred;return;}if (self._deferredState === 1) {self._deferredState = 2;self._deferreds = [self._deferreds, deferred];return;}self._deferreds.push(deferred);return;}handleResolved(self, deferred);
}
  • There is a while loop which will keep assigning the resolved promise object to the current promise which is also a promise for _state === 3

    有一个while循环,它将继续将解析的promise对象分配给当前的promise,这也是_state === 3的promise

  • If _state = 0(pending) and promise state has been deferred until another nested promise is resolved, its callback is stored in self._deferreds

    如果_state = 0(pending)并且承诺状态已推迟到另一个嵌套的承诺被解决,则其回调存储在self._deferreds

function handleResolved(self, deferred) {asap(function() { // asap is external lib used to execute cb immediatelyvar 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);}});
}

What's happening:

发生了什么:

  • If the state is 1(fulfilled) then call the resolve else reject

    如果状态为1 (fulfilled)则调用解决方法 else 拒绝

  • If onFulfilled or onRejected is null or if we used an empty .then() resolved or reject will be called respectively

    如果onFulfilledonRejectednull或如果我们使用一个空.then() 解析拒绝将分别被调用

  • If cb is not empty then it is calling another function tryCallOne(cb, self._value)

    如果cb不为空,则它正在调用另一个函数tryCallOne(cb, self._value)

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

tryCallOne : This function only calls the callback that is passed into the argument self._value. If there is no error it will resolve the promise, otherwise it will reject it.

tryCallOne 此函数仅调用传递到参数self._value的回调。 如果没有错误,它将解决承诺,否则将拒绝它。

Every promise must supply a .then() method with the following signature:

每个Promise必须提供具有以下签名的.then()方法:

promise.then(onFulfilled?: Function,onRejected?: Function
) => Promise
  • Both onFulfilled() and onRejected() are optional.

    onFulfilled()onRejected()都是可选的。

  • If the arguments supplied are not functions, they must be ignored.

    如果提供的参数不是函数,则必须将其忽略。
  • onFulfilled() will be called after the promise is fulfilled, with the promise’s value as the first argument.

    在实现诺言之后,将调用onFulfilled() ,并将诺言的值作为第一个参数。

  • onRejected() will be called after the promise is rejected, with the reason for rejection as the first argument.

    在拒绝承诺后,将调用onRejected()并将拒绝的原因作为第一个参数。

  • Neither onFulfilled() nor onRejected() may be called more than once.

    onFulfilled()onRejected()不得被调用一次以上。

  • .then() may be called many times on the same promise. In other words, a promise can be used to aggregate callbacks.

    .then()可能在同一诺言中被多次调用。 换句话说,promise可以用于聚集回调。

  • .then() must return a new promise.

    .then()必须返回新的诺言。

承诺链 (Promise Chaining)

.then should return a promise. That's why we can create a chain of promises like this:

.then应该兑现承诺。 这就是为什么我们可以创建如下这样的承诺链:

Promise
.then(() => Promise.then(() => Promise.then(result => result) 
)).catch(err)

兑现诺言 (Resolving a promise)

Let’s see the resolve function definition that we left earlier before moving on to .then():

让我们看一下在继续.then()之前我们留下的resolve函数定义:

function resolve(self, newValue) {
// Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedureif (newValue === self) {return reject(self,new TypeError("A promise cannot be resolved with itself."));}if (newValue &&(typeof newValue === "object" || typeof newValue === "function")) {var then = getThen(newValue);if (then === IS_ERROR) {return reject(self, LAST_ERROR);}if (then === self.then && newValue instanceof Promise) {self._state = 3;self._value = newValue;finale(self);return;} else if (typeof then === "function") {doResolve(then.bind(newValue), self);return;}
}self._state = 1;self._value = newValue;finale(self);
}
  • We check if the result is a promise or not. If it’s a function, then call that function with value using doResolve().

    我们检查结果是否是一个承诺。 如果它是一个函数,则使用doResolve()以值调用该函数。

  • If the result is a promise then it will be pushed to the deferreds array. You can find this logic in the finale function.

    如果结果是一个承诺,那么它将被推送到deferreds数组。 您可以在finale功能中找到此逻辑。

拒绝承诺: (Rejecting a promise:)

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

The above function can be found in ./es6-extensions.js.

可以在./es6-extensions.js找到以上功能。

Whenever we reject a promise, the .catch callback is called which is a sugar coat for then(null, onRejected).

每当我们拒绝承诺时, .catch调用.catch回调,这是then(null, onRejected)

Here is the basic rough diagram that I have created which is a birds-eye view of what's happening inside:

这是我创建的基本示意图,它是内部情况的鸟瞰图:

Let’s see once again how everything is working:

让我们再次看看一切如何进行:

For example, we have this promise:

例如,我们有以下承诺:

new Promise((resolve, reject) => {setTimeout(() => {resolve("Time is out");}, 3000)
})
.then(console.log.bind(null, 'Promise is fulfilled'))
.catch(console.error.bind(null, 'Something bad happened: '))
  1. Promise constructor is called and an instance is created with new Promise

    调用Promise constructor ,并使用new Promise创建实例

  2. executor function is passed to doResolve(executor, this) and callback where we have defined setTimeout will be called by tryCallTwo(executor, resolveCallback, rejectCallback)so it will take 3 seconds to finish

    executor函数传递给doResolve(executor, this)tryCallTwo(executor, resolveCallback, rejectCallback)将调用我们定义了setTimeout回调,因此需要3秒钟才能完成

  3. We are calling .then() over the promise instance so before our timeout is completed or any async api returns, Promise.prototype.then will be called as .then(cb, null)

    我们在promise实例上调用.then() ,因此在timeout或任何异步api返回之前, Promise.prototype.then将被称为Promise.prototype.then .then(cb, null)

  4. .then creates a new promise and passes it as an argument to new Handler(onFulfilled, onRejected, promise)

    .then创建一个新的promise并将其作为参数传递给new Handler(onFulfilled, onRejected, promise)

  5. handle function is called with the original promise instance and the handler instance we created in point 4.

    使用原始promise实例和我们在第4点中创建的handler实例调用handle函数。

  6. Inside the handle function, current self._state = 0 and self._deferredState = 0 so self_deferredState will become 1 and handler instance will be assigned to self.deferreds after that control will return from there

    里面的handle功能,目前self._state = 0self._deferredState = 0这样self_deferredState将成为1handler实例将被分配到self.deferreds后控制将回到那里

  7. After .then() we are calling .catch() which will internally call .then(null, errorCallback) — again the same steps are repeated from point 4 to point 6 and skip point 7 since we called .catch once

    .catch()之后.then()我们将调用.catch() ,该方法将在内部调用.then(null, errorCallback) -再次,从点4到点6重复相同的步骤, 并跳过点7,因为我们一次调用.catch

  8. Current promise state is pending and it will wait until it is resolved or rejected. So in this example, after 3 seconds, setTimeout callback is called and we are resolving this explicitly which will call resolve(value).

    当前的promise状态处于挂起状态,它将等待直到解决或拒绝该状态。 因此,在此示例中,在3秒钟后,调用了setTimeout回调,并且我们正在明确解决此问题,这将调用resolve(value)

  9. resolveCallback will be called with value Time is out :) and it will call the main resolve function which will check if value !== null && value == 'object' && value === 'function'

    resolveCallback值将为Time is out :),它将调用主resolve函数,该函数将检查value !== null && value == 'object' && value === 'function'

  10. It will fail in our case since we passed string and self._state will become 1 with self._value = 'Time is out' and later finale(self) is called.

    因为我们通过它会在我们的案例失败stringself._state将成为1self._value = 'Time is out' ,后来finale(self)被调用。

  11. finale will call handle(self, self.deferreds) once because self._deferredState = 1, and for the chain of promises, it will call handle() for each deferred function.

    由于self._deferredState = 1finale将调用一次handle(self, self.deferreds) ,对于诺言链,它将为每个deferred函数调用handle()

  12. In the handle function, since promise is resolved already, it will call handleResolved(self, deferred)

    handle函数中,由于promise已经解决,它将调用handleResolved(self, deferred)

  13. handleResolved function will check if _state === 1 and assign cb = deferred.onFulfilled which is our then callback. Later tryCallOne(cb, self._value) will call that callback and we get the final result. While doing this if any error occurred then promise will be rejected.

    handleResolved功能会检查是否_state === 1和分配cb = deferred.onFulfilled这是我们then回调。 稍后tryCallOne(cb, self._value)将调用该回调,然后得到最终结果。 在执行此操作时,如果发生任何错误,则promise将被拒绝。

当诺言被拒绝时 (When a promise is rejected)

In this case, all the steps will remain the same — but in point 8 we call reject(reason). This will indirectly call rejectCallback defined in doResolve() and self._state will become 2. In the finale function cb will be equal to deferred.onRejected which will be called later by tryCallOne. That’s how the .catch callback will be called.

在这种情况下,所有步骤将保持不变-但在第8点中,我们将其称为reject(reason) 。 这将间接调用rejectCallback doResolve()定义的doResolve()self._state将变为2 。 在finale函数中, cb等于deferred.onRejected ,稍后将由tryCallOne 。 这就是.catch回调将被调用的方式。

That's all for now! I hope you enjoyed the article and it helps in your next JavaScript interview.

目前为止就这样了! 我希望您喜欢这篇文章,并且对您下一次JavaScript采访有所帮助。

If you encounter any problem feel free to get in touch or comment below. I would be happy to help ?

如果您遇到任何问题,请 下面 与我们联系 或发表评论。 我很乐意提供帮助吗?

Don’t hesitate to clap if you considered this a worthwhile read!

如果您认为这值得一读,请随时鼓掌!

Originally published at 101node.io on February 05, 2019.

最初于2019年2月5日发布在101node.io上。

翻译自: https://www.freecodecamp.org/news/how-javascript-promises-actually-work-from-the-inside-out-76698bb7210b/

javascript 作用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/392140.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux 文件理解,对linux中文件系统的理解

首先在linux系统当中一个可被挂在的数据为一个文件系统1.在安装linux过程中我们要进行磁盘分区,可以分根目录/,‘/home‘,‘/boot’,swap等等这些分区,每一个分区(’/(根目录)‘,’/home‘...)就是一个文件系统。2.文件系统分配完…

编译原理—语法分析器(Java)

递归下降语法分析 1. 语法成分说明 <语句块> :: begin<语句串> end <语句串> :: <语句>{&#xff1b;<语句>} <语句> :: <赋值语句> | <循环语句> | <条件语句> <关系运算符> :: < | < | > | > | |…

老笔记整理四:字符串的完美度

今天在宠果网上发现一道题目&#xff0c;求一个字符串的完美度http://hero.pongo.cn/home/index觉得这道题很有趣就挑战了一下&#xff0c;结果没有在规定的1小时里面写完&#xff08;笑&#xff09;&#xff0c;多花了10分钟终于做出来了。题目是这样的&#xff1a;我们要给每…

nlp构建_使用NLP构建自杀性推文分类器

nlp构建Over the years, suicide has been one of the major causes of death worldwide, According to Wikipedia, Suicide resulted in 828,000 global deaths in 2015, an increase from 712,000 deaths in 1990. This makes suicide the 10th leading cause of death world…

域名跳转

案例&#xff1a;当访问lsx.com网站&#xff0c;是我最早论坛的域名。回车之后会自动跳转到lshx.com。 为什么药lsx跳转到lshx.com呢&#xff1f; 为了统一品牌。建议换成了lshx.com。所有之前的lsx.com就不要用了&#xff0c;就让它跳转到lshx.com。是因为之前lsx.com上有很多…

Elastic Stack 安装

Elastic Stack 是一套支持数据采集、存储、分析、并可视化全面的分析工具&#xff0c;简称 ELK&#xff08;Elasticsearch&#xff0c;Logstash&#xff0c;Kibana&#xff09;的缩写。 安装Elastic Stack 时&#xff0c;必须相关组件使用相同的版本&#xff0c;例如&#xff1…

区块链去中心化分布式_为什么渐进式去中心化是区块链的最大希望

区块链去中心化分布式by Arthur Camara通过亚瑟卡马拉(Arthur Camara) 为什么渐进式去中心化是区块链的最大希望 (Why Progressive Decentralization is blockchain’s best hope) 不变性是区块链的最大优势和最大障碍。 逐步分权可能是答案。 (Immutability is blockchain’s…

编译原理—语义分析(Java)

递归下降语法制导翻译 实现含多条简单赋值语句的简化语言的语义分析和中间代码生成。 测试样例 begin a:2; b:4; c:c-1; area:3.14*a*a; s:2*3.1416*r*(hr); end #词法分析 public class analyzer {public static List<String> llistnew ArrayList<>();static …

linux问题总结

linux问题总结 编写后台进程的管理脚本&#xff0c;使用service deamon-name stop的时候&#xff0c;出现如下提示&#xff1a;/sbin/service: line 66: 23299 Terminated env -i LANG"$LANG" PATH"$PATH" TERM"$TERM" "${SERVICEDIR}/${SE…

linux vi行尾总是显示颜色,【转载】Linux 下使用 vi 没有颜色的解决办法

vi 是没有颜色的&#xff0c;vim 是有颜色的。我们可以通过 rpm -qa |grep vim 看看系统中是否安装了下面 3 个 rpm 包&#xff0c;如果有就是安装了 vim 。[rootBetty ~]# rpm -qa |grep vimvim-minimal-7.0.109-7.el5vim-enhanced-7.0.109-7.el5vim-common-7.0.109-7.el5如果…

时间序列分析 lstm_LSTM —时间序列分析

时间序列分析 lstmNeural networks can be a hard concept to wrap your head around. I think this is mostly due to the fact that they can be used for so many different things such as classification, identification or just simply regression.神经网络可能是一个难…

关于计算圆周率PI的经典程序

短短几行代码&#xff0c;却也可圈可点。如把变量s放在PI表达式中&#xff0c;还有正负值的处理&#xff0c;都堪称经典。尤其是处处考虑执行效率的思想令人敬佩。 /* pi/41-1/31/5-1/71/9-…… */ #include <stdio.h> int main(){ int s1; float pi0.,n1.,…

华为产品技术学习笔记之路由原理(一)

路由器&#xff1a;路由器是一种典型的网络连接设备&#xff0c;用来进行路由选择和报文转发。路由器与它直接相连的网络的跳数为0&#xff0c;通过一台路由器可达的网络的跳数为1.路由协议&#xff1a;路由器之间维护路由表的规则&#xff0c;用以发现路由&#xff0c;生成路由…

Linux网络配置:设置IP地址、网关DNS、主机名

查看网络信息 1、ifconfig eth0 2、ifconfig -a 3、ip add 设置主机名需改配置文件&#xff1a; /etc/hosts /etc/sysconfig/network vim /etc/sysconfig/network NETWORKINGyes NETWORKING_IPV6no HOSTNAMEwendyhost Linux配置网络 方法一&#xff1a; 1、使用setup命令进入如…

编译原理—小型(简化)高级语言分析器前端(Java)

实现一个一遍扫描的编译前端&#xff0c;将简化高级语言的部分语法成分&#xff08;含赋值语句、分支语句、循环语句等&#xff09;翻译成四元式&#xff08;或三地址代码&#xff09;&#xff0c;还要求有合理的语法出错报错和错误恢复功能。 测试样例 beginwhile a<b do…

linux boot菜单列表,Bootstrap 下拉菜单(Dropdowns)简介

Bootstrap 下拉菜单是可切换的&#xff0c;是以列表格式显示链接的上下文菜单。这可以通过与 下拉菜单(Dropdown) JavaScript 插件 的互动来实现。如需使用下拉菜单&#xff0c;只需要在 class .dropdown 内加上下拉菜单即可。下面的实例演示了基本的下拉菜单&#xff1a;实例主…

dynamodb管理ttl_如何使用DynamoDB TTL和Lambda安排临时任务

dynamodb管理ttlby Yan Cui崔燕 如何使用DynamoDB TTL和Lambda安排临时任务 (How to schedule ad-hoc tasks with DynamoDB TTL and Lambda) CloudWatch Events let you easily create cron jobs with Lambda. However, it’s not designed for running lots of ad-hoc tasks,…

5g创业的构想_数据科学项目的五个具体构想

5g创业的构想Do you want to enter the data science world? Congratulations! That’s (still) the right choice.您想进入数据科学世界吗&#xff1f; 恭喜你&#xff01; 那(仍然)是正确的选择。 The market currently gets tougher. So, you must be mentally prepared f…

Microsoft Windows Phone 7 Toolkit Silverlight SDK XNA Game Studio 4.0 开发工具套件正式版下载...

Windows Phone 7开发工具套件包括Visual Studio 2010 Express for Windows Phone、Windows Phone模拟器、Expression Blend 4 for Windows Phone、XNA Game Studio 4.0和新增加的必应地图SDK。 英文版的光盘镜像&#xff1a;点击下载 文档中心&#xff1a;Windows Phone develo…

数据挖掘—Apriori算法(Java实现)

算法描述 &#xff08;1&#xff09;扫描全部数据&#xff0c;产生候选1-项集的集合C1&#xff1b; &#xff08;2&#xff09;根据最小支持度&#xff0c;由候选1-项集的集合C1产生频繁1-项集的集合L1&#xff1b; &#xff08;3&#xff09;对k>1&#xff0c;重复执行步骤…