博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
我所知道的Promise
阅读量:6717 次
发布时间:2019-06-25

本文共 6913 字,大约阅读时间需要 23 分钟。

A promise represents the eventual result of an asynchronous operation.

How Use?

The primary way of interacting with a promise is through its then method, which registers callbacks to receive either a promise's eventual value or the reason why the promise cannot be fulfilled.

sample

new Promise((resolve, reject) => {      let a = 1    setTimeout(() => {        if(a === 1){            resolve('resolve')        }else{            reject('reject')        }    }, 1000)}).then((value)=>{    console.log(value)},(err)=>{    console.log(err)})

Why Promise?

在promise还没有出现的时候,在进行异步操作的时候,最常用的方式使用回调函数。但是使用回调函数,会造成回调函数层层嵌套。使得代码不易阅读以及不够优雅。但是如果使用promise之后,可以进行链式调用。代码如下:

// 回调写法asyncFunc1(opt, (...args1) => {      asyncFunc2(opt, (...args2) => {        asyncFunc3(opt, (...args3) => {            asyncFunc4(opt, (...args4) => {                // some operation            });        });    });});// promise 写法new Promise((resolve, reject) => {      resolve(value)}).then((value1) => asyncFunc1()).then((value2) => asyncFun2()).then((value3) => asyncFunc3()).then((value4) => asyncFunc4())

这样看上去,promise链式调用的写法是比回调函数写法更好一些。

在刚开始使用promise到现在,一直有几个问题困扰着我,趁着这几天有空,带着问题出发,看下源码是怎么实现的。

1 为什么说promise是立刻执行的对象?

2 promise中发生异常时,是如何被捕获到的?

3 promise如何根据状态不同调用不同的回调函数?

4 promise中的then是如何实现链式调用的?

本文只分析core.js文件,只关心作者自身想探究的问题,其他细节问题没有阐述,请见谅。代码来自 github:then/promise

 

先解决第一个问题,promise如何立即执行,当然是直接进到promise的构造函数里面去。

function Promise(fn) {   if (typeof this !== 'object') {   throw new TypeError('Promises must be constructed via new'); } if (typeof fn !== 'function') {   throw new TypeError('Promise constructor\'s argument is not a function'); } this._deferredState = 0; this._state = 0; // 当前状态 this._value = null; // resolve 返回的结果 this._deferreds = null;  if (fn === noop) return; doResolve(fn, this);}/*** 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 res = tryCallTwo(fn, function (value) {   if (done) return;   done = true;   resolve(promise, value); }, function (reason) {   if (done) return;   done = true;   reject(promise, reason); }); if (!done && res === IS_ERROR) {   done = true;   reject(promise, LAST_ERROR); }}function tryCallTwo(fn, a, b) {   try {   fn(a, b); } catch (ex) {   LAST_ERROR = ex;   return IS_ERROR; }}var LAST_ERROR = null; //存储的是最后的异常  var IS_ERROR = {}; //表示不成功,包括程序异常或者reject状态

以上有三个方法,过程是这样子的。调用Promise的构造函数,中间除了做一些状态属性声明,初始化和对参数的判断外,最后会调用doResolve方法。这里又调用了tryCallTwo方法,将promise内部定义的resolve和reject作为参数,传给了fn执行。 所以说Promise是立即执行的,问题1得到了验证

在tryCallTwo函数体中,使用了try catch 捕获了异常,并且返回了ISERROR,表示发生了异常。然后通过判断当前异步函数执行结果是否为ISERROR,来执行reject函数。 举个例子:

new Promise((resolve, reject) => {      throw new Error()}).then((value)=>{    console.log(value)},(err)=>{    console.log('error')})// 输出结果是error

promise在内部发生异常的时候,是通过try catch捕获到之后通过返回异常状态,最后调用reject函数,得到了问题2的答案。

说到promise里面的状态,其中有四种状态

// 0 pending // 1 - fulfilled with _value// 2 - rejected with _value// 3 - adopted the state of another promise, _value// 其中3 表示的是state传给下一个promise
那接下去看下resolve 和 reject 两个函数。

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.')   ); } 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);}function reject(self, newValue) {   self._state = 2; self._value = newValue; if (Promise._onReject) {   Promise._onReject(self, newValue); } finale(self);}
 

resolve方法主要对传入的newValue进行了判断。分别为newValue和promise本身相等,newValue为object或者function类型,newValue是另一个Promise对象和newValue为普通数据类型四种状态。函数细节不细看。主要看当newValue为普通数据类型的时候。将state赋值为1,value赋值为参数newValue.同时调用了finale函数。接下去看finale函数。为了节约篇章,这里reject就不再多说了。

function finale(self) {   if (self._deferredState === 1) {   handle(self, self._deferreds);   self._deferreds = null; } if (self._deferredState === 2) {   for (var i = 0; i < self._deferreds.length; i++) {     handle(self, self._deferreds[i]);   }   self._deferreds = null; }}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);}

在finale函数内部对deferredState进行了判断。但是在handle函数里面我们发现,对state为0的时候,会有赋值操作,但是到finale这个函数为止,并没有对deferredState有修改值的操作,deferredState仍然为0.所以,过程就在finale中止了。这样,new Promise过程就结束了,最后结果好像仅仅是对value和state进行了赋值操作。

虽然知道了state和value改变了,但是我们还不知道改变了_state有什么用。我们暂且先把这个问题放下,去看看其他没看到的代码。首先找到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;};
 

then函数调用到了handle函数,这个就是之前被中断掉的函数。神奇诶,又回到了之前断掉的函数。这里把定义在then函数里面的两个回调传给了handle函数。在handle函数里面,最后执行了handleResolved函数。来看看handleResolved函数

function handleResolved(self, deferred) {   asap(function() {   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);   } });}function tryCallOne(fn, a) {   try {   return fn(a); } catch (ex) {   LAST_ERROR = ex;   return IS_ERROR; }}

handleResolved函数里面做了件什么事情呢?根据state状态,去拿到resolved或者rejected状态的回调函数。如果回调函数没有定义的话,则把原来的promise和value传给resolve或者reject函数。但是如果定义了回调函数的话,将promise和_value传给回调函数执行。将执行的结果作为参数传给resolve函数。这样,就产生了与之前一样的流程。诶,这好像就是我们要找的链式?

回顾一下

在new Promise过程中

1 doResolve

2 resolve || reject // 更改value和state

3 finale // 中断

如果这个时候promise有调用then方法的话

4 handle

5 handleResolved

6 resolve || reject

7 finale // 中断

如果这个时候then后面还有then的话,则继续上面的4,5,6,7步操作。因为then返回的是一个promise对象。所以后面可以继续then方法。

上面的流程,可以看到promise的链式调用了。找到了问题4的答案

同时,在resolve里面变更后的state在handleResolved函数里面用到了。用于调用不同的回调函数。也找到了问题3的答案。

原文发布时间为:2018年07月02日
本文链接:     如需转载请联系原作者
 
你可能感兴趣的文章
《企业大数据系统构建实战:技术、架构、实施与应用》一1.3 本章小结
查看>>
为什么不能用memcached存储Session?
查看>>
《C++编程风格(修订版)》——2.2 明确定义的状态
查看>>
页面加载显示进度条
查看>>
Logstash 日志搜集处理框架 安装配置
查看>>
Manifest.xml 入门基础(一) 概述与&lt;manifest&gt;标签
查看>>
2016全球最强数据库大盘点
查看>>
可视化与领域驱动设计
查看>>
数据结构实践——字符串加密
查看>>
其他转移指令(0904)
查看>>
《卸甲笔记》-多表查询之一
查看>>
安装部署nvm、npm、nodejs之前先了解清楚三者之间关系
查看>>
linux 磁盘管理下(LVM逻辑卷创建和管理,磁盘配额设置方法以及小技巧)
查看>>
NFS Volume Provider(Part III) - 每天5分钟玩转 OpenStack(64)
查看>>
MySQL 安装详解
查看>>
使用Express + Socket.io + MongoDB实现简单的聊天室
查看>>
【cocos2d-x】横向滚屏射击游戏②----虚拟控制手柄
查看>>
Docker 之 容器网络管理
查看>>
基于时间点的恢复
查看>>
中国五大顶级域名8月第三周新增2.2万 美国净减5.4万个
查看>>