欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

一步一步了解Promise原理

发布时间:2024/4/13 编程问答 40 豆豆
生活随笔 收集整理的这篇文章主要介绍了 一步一步了解Promise原理 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

 最近看到一篇有关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原理的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。