最近看到一篇有关promise的文章还不错,想着翻译过来看看,顺便也算是对promise作个总结。

好了,开门见山,跟着我的脚步一起来学习吧。

一、最简单的例子

1、先看例子1:

function doSomething(callback) {var value = 42;callback(value);
}doSomething(function(value) {console.log('Got a value:' + value);
});复制代码

这是一个传入函数作为回调函数的函数,调用回调函数得到value值。

需要做些什么改变让它看起来像‘promise’呢?来看例子2

2、例子2:

function doSomething() {return {then: function(callback) {var value = 42;callback(value);}};
}
doSomething().then(function(value) {console.log('Got a value:' + value);
});复制代码

例2是在例1的基础上做修改:在函数体内直接返回一个对象,然后对象里面有then,其value为function。

使用上是在.then()里传入一个回调函数。并且得到then中的value(42)。这使用看起来和promise好像一样了。

promise有个重要的思想:Promises capture the notion of an eventual value into an object。

这句话的意思是promise捕捉最终值传入到一个对象中。

读到这里也许有些困惑,没关系,一起继续探索强大有趣的事情吧!!!(老外的语言就是这么直白简单hhhhhh...)

二、定义Promise类

1、现在开始,进入正题。定义一个简单的Promise类。

function Promise(fn) {var callback = null;this.then = function(cb) {callback = cb;};function resolve(value) {callback(value);}fn(resolve);
}function doSomething() {return new Promise(function(resolve) {var value = 42;resolve(value);});
}复制代码

来调用一下:

doSomething().then(function(res){console.log(res);
})//来分析一下执行顺序:
// doSomething -> new Promise -> fn -> resolve -> callback复制代码

emm...执行到这里,报错了??  callback是null

为什么呢? 因为callback是在then()方法中被赋值的,现在resolve()早于then()执行,callback还是null的状态。

那怎么办呢?

我们用setTimeout来解决这个问题。

function Promise(fn) {var callback = null;this.then = function(cb) {callback = cb;};function resolve(value) {// force callback to be called in the next// iteration of the event loop, giving// callback a chance to be set by then()setTimeout(function() {callback(value);}, 1);}fn(resolve);
}复制代码

好了 现在我们再来分析下执行过程。

doSomething -> new Promise -> fn -> resolve -> then -> callback复制代码

这样是顺序能够正常运行了,但是我觉得这个代码糟糕透了

来下面的例子:

var promise = doSomething()setTimeout(function() {promise.then(function(value) {log("got a value", value);});
复制代码

如果我们是这样调用呢,是不是又要报错了!

callback和then()先后被放入异步执行队列中,根据先进先出原则,callback先执行,可此时callback is not a function but null.

我们的这个尝试以失败告终,接着来...

2、给Promise添加状态

  • A promise can be **pending** waiting for a value, or **resolved** with a value.
  • Once a promise resolves to a value, it will always remain at that value and never resolve again.

这两句话的意思是一个promise要么处于pending状态(等待结果),要么已经resolved(得到结果),promise一旦resolved得到结果,它就一直保持这个结果不会再执行resolve。

(promise的reject稍后讨论)

function Promise(fn) {var state = 'pending';var value;var deferred;function resolve(newValue) {value = newValue;state = 'resolved';if(deferred) {handle(deferred);}}function handle(onResolved) {if(state === 'pending') {deferred = onResolved;return;}onResolved(value);}this.then = function(onResolved) {handle(onResolved);};fn(resolve);
}
function doSomething() {return new Promise(function(resolve) {var value = 42;resolve(value);});
}function doSomethingElse() {return new Promise(function(resolve) {var value = 3;setTimeout(function(){resolve(value);},1);});
}
复制代码

我们来调用一下:

测试1:
doSomething().then(function(value) {log("got a value", value);
});//分析一下过程:
doSomething -> Promise -> fn -> resolve -> then -> handle -> onResolved复制代码
测试2:
var promise = doSomething();
setTimeout(function(){promise.then(function(value){log("got a value", value);})
})
//过程分析
doSomething -> Promise -> fn -> resolve -> then -> handle -> onResolved复制代码
测试3:
var promiseElse = doSomethingElse();
promiseElse.then(function(value){log("got a value", value);
});
//过程分析
doSomethingElse -> Promise -> fn -> then -> handle(deferred缓存onResolved) ->setTimeout异步队列开始执行,resolve执行 -> handle(deferred)复制代码

以上测试无论何时调用then()和resolve(),根据state状态的判断,可以收放自如地完成同步异步函数执行。

With promises, the order in which we work with them doesn't matter. We are free to call `then()` and `resolve()` whenever they suit our purposes. This is one of the powerful advantages of capturing the notion of eventual results into an object

显然,到这里我们已经实现了Promise强大的pending-->resolved功能。

接下来我们要干什么呢?

三、链式Promises

平时我们对Promise的链式调用使用得很多吧,即使没使用过链式调用那也一定看到过哦。

`then()` always returns a promise

根据这个原则,我们稍作修改了代码

function Promise(fn) {var state = 'pending';var value;var deferred = null;function resolve(newValue) {value = newValue;state = 'resolved';if(deferred) {handle(deferred);}}function handle(handler) {if(state === 'pending') {deferred = handler;return;}if(!handler.onResolved) {handler.resolve(value);return;}var ret = handler.onResolved(value);handler.resolve(ret);}this.then = function(onResolved) {return new Promise(function(resolve) {handle({onResolved: onResolved,resolve: resolve});});};fn(resolve);
}复制代码

这里我们注意到:then()返回以个新的Promise对象,这使得我们在实例化一个Promise对象之后调用then()时还可以接着调用then(),形成了链式调用。

下面我们来看下测试用例:

doSomething().then(function(result) {console.log('first result', result);return 88;
}).then(function(secondResult) {console.log('second result', secondResult);
});
// first result 42
// second result 88复制代码

1、参数可选

当然then()方法里面的参数是可选,我们也可能遇到这样情况:

doSomething().then().then(function(result) {console.log('got a result', result);
});
// got a result 42
//这里第一个then没有回调函数 但是第二个then还是能得到结果
//执行顺序:
doSomething -> Promise -> fn -> resolve -> then -> handle -> handler.resolve(42) -> then
-> handle -> handler.onResolve(42) -> handler.resolve(undefined)复制代码

这里的第一个then没有传入回调函数,所以直接执行handler.resolve()将value保存以待第二个then获取。

2、在链中返回Promise

doSomething().then(function(result) {// doSomethingElse returns a promisereturn doSomethingElse(result);
}).then(function(finalResult) {console.log("the final result is", finalResult);
});复制代码

这里的finalResult按照预期的结果是返回了Promise实例,但是如果我们想要doSomethingElse中resolved值怎么办?

修改一下resolve函数

function resolve(newValue) {if(newValue && typeof newValue.then === 'function') {newValue.then(resolve);return;}state = 'resolved';value = newValue;if(deferred) {handle(deferred);}
}复制代码

我们保持resolve()中value值不是一个promise实例而是一个普通值,所以在value赋值之前再做一次.then的操作,直到得到value的值为一个普通值。

附上完整代码,执行一遍。

function Promise(fn) {var state = 'pending';var value;var deferred = null;function resolve(newValue) {if (newValue && typeof newValue.then === 'function') {newValue.then(resolve);return;}state = 'resolved';value = newValue;if (deferred) {handle(deferred);}}function handle(handler) {if (state === 'pending') {deferred = handler;return;}if (!handler.onResolved) {handler.resolve(value);return;}var ret = handler.onResolved(value);handler.resolve(ret);}this.then = function (onResolved) {return new Promise(function (resolve) {handle({onResolved: onResolved,resolve: resolve});});};fn(resolve);
}function doSomething() {return new Promise(function (resolve) {var value = 42;resolve(value);});
}function doSomethingElse(value) {return new Promise(function (resolve) {resolve("did something else with " + value);});
}doSomething().then(function (firstResult) {log("first result:", firstResult);return doSomethingElse(firstResult);
}).then(function (secondResult) {log("second result:", secondResult);
});//first result: 42//second result: did something else with 42
//执行顺序分析:
doSomething -> Promise -> fn -> resolve -> then -> Promise -> fn -> handle -> handler.onResolved
-> handler.resolve(doSomethingElse(firstResult)) -> newValue.then -> Promise -> fn -> handle(这里的onResolved传入的是上一个then的resolve)
-> handler.onResolved 这里完成对上一个then的value赋值 -> handler-resolve -> then(这里开始第二个then)复制代码

这里过程有点绕,不过慢慢分析可以理清楚这中间value传递。

四、reject处理

终于要加上reject部分的处理啦 其实和resolve部分差不多。当程序运行错误时,我们需要获知为什么产生了错误,那么在promise中我们知道错误的发生呢??

这时需要then的第二个回调函数!

function Promise(fn) {var state = 'pending';var value;var deferred = null;function resolve(newValue) {if(newValue && typeof newValue.then === 'function') {newValue.then(resolve, reject);return;}state = 'resolved';value = newValue;if(deferred) {handle(deferred);}}function reject(reason) {state = 'rejected';value = reason;if(deferred) {handle(deferred);}}function handle(handler) {if(state === 'pending') {deferred = handler;return;}var handlerCallback;if(state === 'resolved') {handlerCallback = handler.onResolved;} else {handlerCallback = handler.onRejected;}if(!handlerCallback) {if(state === 'resolved') {handler.resolve(value);} else {handler.reject(value);}return;}var ret = handlerCallback(value);handler.resolve(ret);}this.then = function(onResolved, onRejected) {return new Promise(function(resolve, reject) {handle({onResolved: onResolved,onRejected: onRejected,resolve: resolve,reject: reject});});};fn(resolve, reject);
}复制代码

我们来调用一下:

function doSomething() {return new Promise(function(resolve, reject) {var result = somehowGetTheValue();if(result.error) {reject(result.error);} else {resolve(result.value);}});
}复制代码

As mentioned earlier, the promise will transition from **pending** to either **resolved** or **rejected**, never both. In other words, only one of the above callbacks ever gets called

上面doSomething调用时,我们发现Promise的回调方法中,如果有错误发生,那么调用reject();如果没有错误发生,那么调用resolve()。Promise会从pending到resolved,或者从pending到rejected,只有这两种情况,而且只能发生其一,也就是说要么成功,要么失败。

意想不到的错误我们也要处理。

刚刚我们调用reject()方法是在我们能知道的情况下,但是万一在promise中发生错误我们应该怎么办呢?

这里有两个采取捕捉错误的方法。

1、在resolve中可能发生错误进行捕捉

function resolve(newValue) {try {// ... as before} catch(e) {reject(e);}
}复制代码

2、在handle方法中进行错误捕捉

function handle(deferred) {// ... as beforevar ret;try {ret = handlerCallback(value);} catch(e) {handler.reject(e);return;}handler.resolve(ret);
}复制代码

文章到这里,reject的部分也完成了。如果你对done()、all()、race()等方法感兴趣你可以去看promise的官方API。

github上有学习的代码:https://github.com/Turboemily/learn_promise_development.git

以上内容来自文章:https://www.mattgreer.org/articles/promises-in-wicked-detail/#defining-the-promise-type。

一步一步了解Promise原理相关推荐

  1. 一步一步手绘Spring MVC运行时序图(Spring MVC原理)

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  2. javascript 函数 add(1)(2)(3)(4)实现无限极累加 —— 一步一步原理解析

    问题:我们有一个需求,用js 实现一个无限极累加的函数, 形如 add(1) //=> 1; add(1)(2)  //=> 2; add(1)(2)(3) //=>  6; add ...

  3. 30分钟,让你彻底明白Promise原理

    原文链接 前言 前一阵子记录了promise的一些常规用法,这篇文章再深入一个层次,来分析分析promise的这种规则机制是如何实现的.ps:本文适合已经对promise的用法有所了解的人阅读,如果对 ...

  4. ES6 Promise原理

    ES6 Promise原理 一.Promise是什么 二.为什么会有Promise 1.回调地狱 + 异步同步事件调用顺序带来的双重伤害 2.回调事件的分离 三.Promise的三种状态 1.reso ...

  5. Promise原理及手写Promise

    原理: Promise 原理围绕以下三个问题进行解决: (有任何一步不了解的话请先往下看手写Promise,会一步步剖析原理,看完后再继续回顾这里!!) 1. 怎么实现异步? Promise内部the ...

  6. Promise原理分析

    前言 最近讨论到了 Promise,此前知道也使用过它,但是对于其原理却不甚了解. 于是翻了翻 MDN 上的文档,又找了几篇文章看了看,研究了研究. 最终,自己尝试了一番,对于其原理也有所了解. Pr ...

  7. Promise原理及实现

    Promise原理及实现 1 Promise核心逻辑实现 2 加入异步逻辑 3 then方法添加多次调用逻辑 4 链式调用then方法 5 Promise错误捕获 6 then方法参数设置为可选 7 ...

  8. 调试JDK源码-一步一步看HashMap怎么Hash和扩容

    调试JDK源码-一步一步看HashMap怎么Hash和扩容 调试JDK源码-ConcurrentHashMap实现原理 调试JDK源码-HashSet实现原理 调试JDK源码-调试JDK源码-Hash ...

  9. 【深度学习基础】一步一步讲解卷积神经网络

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送 本文转自:一步一步讲解卷积神经网络 卷积神经网络(Convoluti ...

  10. 基于postfix一步一步构建Mailserver,支持虚拟用户,支持WebMail

    我们来一步一步来构建MailServer,支持虚拟用户.虚拟域,支持Webmail,支持Mysql.这个实验化了两天的时间完成的,其中各种崎岖,认真的照着做,问题不大.不过新手还是不要做这个了,需要整 ...

最新文章

  1. Oracle 一些常用的数据字典
  2. 傲腾内存 可以用ghost系统_玩机小贴士:Intel傲腾内存你用过没有?
  3. 工作占用了太多私人时间_职晓|如果工作占用了生活时间,我应不应该辞职?...
  4. 1.0Nvm环境配置
  5. JAVA15.JDK15新特性.4 TextBlock
  6. bom展开算法 java程序实现_java实现aprior算法
  7. iPhone 12 5G更耗电?续航时间较4G妥妥地缩短不少
  8. mysql 监控工具安装_Mysql监控工具Innotop工具安装
  9. 调用Kubernetes SDK实现二次开发
  10. 一剪梅·红藕香残玉簟秋
  11. 实现百度语音识别功能
  12. OSChina 周六乱弹 —— 女菩萨,你可愿做贫僧的……
  13. Combating Spyware in the Enterprise
  14. JACTF misc wp
  15. matlab引用csv文件,如何用MATLAB读取csv文件
  16. python分析红楼梦中人物形象_《红楼梦》中女性人物形象分析
  17. C语言中的hook和bait设计
  18. 构建之法前三章读后感—软件工程
  19. 618商战大片谢幕,销量冠军竟然有两个?
  20. AppletViewer使用方法介绍

热门文章

  1. 数据可视化:浅谈热力图如何在前端实现
  2. VC中显示ICON和BMP图片
  3. idea autoscroll from source
  4. (动态,静态)(解释,编译)(强类型,弱类型)
  5. [Z]如何使用OpenGL扩展
  6. 精通Android3笔记--第十一章
  7. java压缩----使用ANT JDK压缩---解决中文问题
  8. 每个Java应用容器都要包含tomcat_部署一个不依赖tomcat容器的应用
  9. gephi java教程_Gephi的使用--以社交网络图为例
  10. 搭建K8s集群(kubeadm方式)-部署master节点