Home >Web Front-end >JS Tutorial >A detailed introduction to Promises in JavaScript
JavaScript is single-threaded and can only execute one task at a time. When a task takes a long time, subsequent tasks must wait. So, is there any way to solve this kind of problem? (Leave aside WebWorker), that is to let the code execute asynchronously. What does it mean? For example, when making an Ajax asynchronous request, the value of readyState is continuously monitored to determine the execution of the specified callback function.
There are usually three types of asynchronous execution, callback functions, event listening, and publish and subscribe. Event listening and publish and subscribe are actually similar, but the latter is more robust.
Such as callback function, callback function is the simplest programming idea applied in asynchronous execution. As follows:
function async(item,callback){ console.log(item); setTimeout(function(){ callback(item+1); },1000); }
In the above example, when the async function is executed, the printing operation is completed, and the callback function is executed after 1 second (but not necessarily 1 second, see "setTimeout things" for details).
The main purpose of asynchronous is to handle non-blocking and improve performance. Imagine if an operation requires multiple async function operations, as follows:
async(1, function(item){ async(item, function(item){ async(item, function(item){ console.log('To be continued..'); }); }); });
Isn’t it a bit difficult to read?
For another example, in order to make the above code more robust, we can add exception capture. In asynchronous mode, exception handling is distributed in different callback functions. We cannot handle exceptions through try...catch when calling, so it is difficult to do it effectively and clearly.
Ouch, what should I do?
唔唔唔唔, 唔唔唔唔—Promise makes its debut.
If you use ES6 Promise to optimize the above code, you can get:
function opration(item){ var p = new Promise(function(resolve, reject){ setTimeout(function(){ resolve(item+1); },1000); }); console.log(item); return p; } function failed(e){ console.log(e); } Promise.resolve(1).then(opration).then(opration).then(opration).catch(failed);
Using Promise The optimized code has obvious advantages, turning the callback function into a chain call , avoids layers of nesting, makes the program flow clear, and handles errors thrown by one or more callback functions uniformly through the catch method.
Oh, that’s good. So who is this Promise in ES6? What are the specific usage rules? Let's explore it together.
Promise is a solution for asynchronous programming, which is more reasonable than traditional solutions (callbacks and events) and more powerful. It was first proposed and implemented by the community. ES6 wrote it into the language standard, unified its usage, and provided Promise objects natively.
Promise The object has and only three states:
1, pending: the asynchronous operation is not completed.
2, resolved: The asynchronous operation has been completed.
3, rejected: The asynchronous operation failed.
Also, there are only two modes of change in these three states, and once the state changes, it will not change again:
1, Asynchronous operation from pending to resolved ;
2, Asynchronous operation from pending to rejected;
Okay, since it belongs to the ES6 specification, we can directly print out the Promise through chrome and take a look This thing:
Well, It is clear at a glance that Promise is a constructor, Ook, so through it, we can instantiate our own Promise object , and make use of it.
Since Promise is a constructor, let’s take a look at the new one first. As follows:
var p = new Promise();
and execute the above code, the chrome screenshot is as follows:
Why is the error reported?
Oh, by the way, the Promise constructor requires a function as its parameter, and the function as a parameter has two parameters. The function of the first parameter is when the asynchronous operation changes from pending to resolved. for it to be called when; the second parameter is used for it to be called when the asynchronous operation changes from pending to rejected.
For example, I named the first parameter of the anonymous function resolve; the second parameter was named reject. The Demo is as follows:
var p = new Promise(function(resolve, reject){ console.log('new一个Promise对象'); setTimeout(function(){ resolve('Monkey'); },1000); });
and execute the above code. The chrome screenshot is as follows:
##Special reminder: When passing in an anonymous function as a constructor When we use new as the parameter of function Promise, the anonymous function has already been executed, as shown in the figure above.
Hey, in the above code, we use the setTimeout timer in the anonymous function and call resolve after 1 second. Why is there no undefined or error reported? ! This is the power of Promise. Because of this, we can rewrite asynchronous operations into elegant chain calls. How to call it? Remember, in the "Promise Overview" section, we printed Promise through chrome and used the area in the red line box? Among them, there is a then method (Promise.prototype.then) in the Promise prototype. Through this then method, it is enough. As follows:p.then(function(value){ console.log(value); });Among them, the then method has two anonymous functions as its parameters, which correspond to the resolve and reject parameters of Promise. Execute the code and the result is as follows:
好了,当then执行完后,如果我们想继续在其之后看,使用then方法链式调用,有两种情况,一种是直接返回非Promise对象的结果;另一种是返回Promise对象的结果。
1、返回非Promise对象的结果:紧跟着的then方法,resolve立刻执行。并可使用前一个then方法返回的结果。如下:
p.then(function(value){ console.log(value); //返回非Promise对象,如我的对象 return { name: 'Dorie', age: 18 }; }).then(function(obj){ console.log(obj.name); });
执行上述完整代码,chrome截图如下:
2、返回Promise对象的结果:紧跟着的then方法,与new Promise后的then方法一样,需等待前面的异步执行完后,resolve方可被执行。如下:
p.then(function(value){ var p = new Promise(function(resolve, reject){ setTimeout(function(){ var message = value + ' V Dorie' resolve(message); },1000); }); console.log(value); //返回一个Promise对象 return p; }).then(function(value){ console.log(value); });
执行上述完整代码,chrome截图如下:
那么,当创建、执行Promise方法中有异常报错,如何捕获呢?
Promise.prototype.catch原型方法,就是为其而设定的。它具有冒泡的特性,比如当创建Promise实例时,就出错了,错误消息就会通过链式调用的这条链,一直追溯到catch方法,如果找到尽头都没有,就报错,并且再找到catch之前的所有then方法都不能执行了。Demo如下(代码太长,请自行展开):
<!DOCTYPE html> <head> <title>test</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> var p = new Promise(function(resolve, reject){ //M未定义 console.log(M); setTimeout(function(){ resolve('Monkey'); },1000); }); p.then(function(value){ var p = new Promise(function(resolve, reject){ setTimeout(function(){ var message = value + ' V Dorie' resolve(message); },1000); }); console.log(value); //返回一个Promise对象 return p; }).then(function(value){ console.log(value); return 'next is catch'; }).catch(function(e){ console.log(e); }).then(function(value){ console.log('execute,but value is ' + value); }); </script> </body> </html>
执行上述代码,chrome截图如下:
好了,到这里,我们已经了解了最常用的Promise.prototype.then和Promise.prototype.catch这两个原型方法。另外,像Promise构造函数还有属于自身的方法,如all、rece、resolve、reject等,详情请点击这里(here)。
通过一路上对Promise的讲述,我们也有了一定的认识,其实Promise并没有想象中的那么难以理解嘛。懂得Promise概念后,其实我们自己也可以实现一个简易版的Promise。下面就一同尝试实现一个呗。
假设:有三个异步操作方法f1,f2,f3,且f2依赖于f1,f3依赖于f2。如果,我们采用ES6中Promise链式调用的思想,我们可以将程序编写成这样:
f1().then(f2).then(f3);
那么,通过上面这一系列链式调用,怎样才能达到与ES6中Promise相似的功能呢?
初步想法:首先将上述链式调用的f2、f3保存到f1中,当f1中的异步执行完后,再调用执行f2,并将f1中的f3保存到f2中,最后,等f2中的异步执行完毕后,调用执行f3。详细构思图,如下:
从上图可知,由于f1、f2 、f3是可变得,所以存储数组队列thens,可放入,我们即将创建的模拟Promise构造函数中。具体实现代码如下:
//模拟Promise function Promise(){ this.thens = []; }; Promise.prototype = { constructor: Promise, then: function(callback){ this.thens.push(callback); return this; } };
并且,需要一个Promise.prototype.resolve原型方法,来实现:当f1异步执行完后,执行紧接着f1后then中的f2方法,并将后续then中方法,嫁接到f2中,如f3。具体实现代码如下:
//模拟Promise,增加resolve原型方法 function Promise(){ this.thens = []; }; Promise.prototype = { constructor: Promise, then: function(callback){ this.thens.push(callback); return this; }, resolve: function(){ var t = this.thens.shift(), p; if(t){ p = t.apply(null,arguments); if(p instanceof Promise){ p.thens = this.thens; } } } };
测试代码(代码太长,自行打开并运行)。
function f1() { var promise = new Promise(); setTimeout(function () { console.log(1); promise.resolve(); }, 1500) return promise; } function f2() { var promise = new Promise(); setTimeout(function () { console.log(2); promise.resolve(); }, 1500); return promise; } function f3() { var promise = new Promise(); setTimeout(function () { console.log(3); promise.resolve(); }, 1500) return promise; } f1().then(f2).then(f3);
仔细品味,上述实现的Promise.prototype.resolve方法还不够完美,因为它只能够满足于f1、f2、f3等方法都是使用模拟的Promise异步执行的情况。而,当其中有不是返回的Promise对象的呢,而是返回一个数字,亦或是什么也不返回,该怎么办?所以,针对以上提出的种种可能,再次改进resolve。改善代码如下:
//模拟Promise,改善resolve原型方法 var Promise = function () { this.thens = []; }; Promise.prototype = { constructor: Promise, then: function(callback){ this.thens.push(callback); return this; }, resolve: function () { var t,p; t = this.thens.shift(); t && (p = t.apply(null, arguments)); while(t && !(p instanceof Promise)){ t = this.thens.shift(); t && (p = t.call(null, p)); } if(this.thens.length){ p.thens = this.thens; }; } }
测试代码(代码太长,自行打开并运行)。
function f1() { var promise = new Promise(); setTimeout(function () { console.log(1); promise.resolve(); }, 1500) return promise; } function f2() { var promise = new Promise(); setTimeout(function () { console.log(2); promise.resolve(); }, 1500); return promise; } function f3() { var promise = new Promise(); setTimeout(function () { console.log(3); promise.resolve(); }, 1500) return promise; } function f4() { console.log(4); return 11; } function f5(x) { console.log(x+1); } function f6() { var promise = new Promise(); setTimeout(function () { console.log(6); promise.resolve(); }, 1500) return promise; } function f7() { console.log(7); } var that = f1().then(f2).then(f3).then(f4).then(f5).then(f6).then(f7);
好了,初步模拟的Promise就OK啦。
吼吼,对于Promise,我们这一路走来,发现原来也不过如此呢。
以上就是详细介绍JavaScript 中的 Promise的内容,更多相关内容请关注PHP中文网(www.php.cn)!