下面开始:
假设现在一个日常开发会遇到这样一个需求:多个接口异步请求,第二个接口依赖于第一个
接口执行完毕之后才能利用数据进行一系列操作。一般会这样写:
A.fetchData({ url: 'http://......', success: function (data) { A.fetchData({ // 要在第一个请求成功后才可以执行下一步 url: 'http://......', success: function (data) { // ...... } }); } });这样写没问题,但是有两个缺点:
1、当有多个操作的时候,会导致多个回调函数嵌套,不够美观
2、如果有几个操作没有前后顺序之分时,例如上面的后一个请求不依赖与前一个请求的返回结果的时候,
同样也需要等待上一个操作完成再实行下一个操作。
从ES6开始,Promise对象可以解决上述问题。
一个Promise对象可以理解为一次将要执行的操作,使用了Promise对象之后可以用一种链式调用的方式
来组织代码,让代码更加直观。
先看代码:
function helloWorld (ready) { return new Promise(function (resolve, reject) { if (ready) { resolve("Hello World!"); } else { reject("Good bye!"); } }); } helloWorld(true).then(function (message) { alert(message); }, function (error) { alert(error); });上面的代码实现的功能非常简单,helloWord 函数接受一个参数,如果为 true 就打印 "Hello World!",
如果为 false 就打印错误的信息。helloWord 函数返回的是一个 Promise 对象。
在 Promise 对象当中有两个重要方法————resolve 和 reject。
resolve 方法可以使 Promise 对象的状态改变成成功,同时传递一个参数用于后续成功后的操作,
在这个例子当中就是 Hello World! 字符串。
reject 方法则是将 Promise 对象的状态改变为失败,同时将错误的信息传递到后续错误处理的操作。
Promise 对象有三种状态: 1.Fulfilled 可以理解为成功的状态 2.Rejected 可以理解为失败的状态 3.Pending 既不是 Fulfilld 也不是 Rejected 的状态,可以理解为 Promise 对象实例创建时候的初始状态。
helloWorld 的例子中的 then方法就是根据 Promise 对象的状态来确定执行的操作,resolve 时执行第一个
函数(onFulfilled),reject 时执行第二个函数(onRejected)。promise模式在任何时刻都处于以下三种状态之一:未完成(unfulfilled)、已完成(resolved)和拒绝(rejected)。以CommonJS Promise/A 标准为例,promise对象上的then方法负责添加针对已完成和拒绝状态下的处理函数。then方法会返回另一个promise对象,以便于形成promise管道,这种返回promise对象的方式能够支持开发人员把异步操作串联起来,如then(resolvedHandler, rejectedHandler); 。resolvedHandler 回调函数在promise对象进入完成状态时会触发,并传递结果;rejectedHandler函数会在拒绝状态下调用。
示例代码1:
function printHello (ready) { var promise = new Promise(function (resolve, reject) { if (ready) { resolve("Hello"); } else { reject("Good bye"); } }); return promise; } function printWorld () { console.log('World'); } function printExclamation () { console.log('!!!'); } printHello(true) .then(function(message) .then(printWorld) .then(printExclamation) .catch(function(error){ console.log(error); });;函数先执行printHello,返回一个promise对象,通过then将异步操作串联起来。 执行结果应该是 Hello World !!!
示例代码2:
function helloWorld (ready) { return new Promise(function (resolve, reject) { if (ready) { resolve("Hello World!"); } else { reject("Good bye!"); } }); } var _this = this; printHello(true) .then(function (message) { var REQUEST_URL = 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json'; return fetch(REQUEST_URL) .then((response) => response.json()) .then((responseData) => { var movie = responseData.movies[1]; console.log('data = ', movie.title); return movie.title; }) },function (error) { return(error); }).then(function (message) { return message + ' World'; }).then(function (message) { return message + '!!!'; }).then(function (message) { console.log(message); console.log('finally'); }).catch(function(error){ console.log(error); });上面的代码中有两个promise,第一个promise执行完毕后也就是printHello之后,会执行下一个then,
这个then返回了一个获取数据的promise,后面的then拿到的promise都是指向这个promise对象的。上述例子通过链式调用的方式,按顺序打印出了相应的内容。then 可以使用链式调用的写法原因在于,每一次执行该方法时总是会返回一个 Promise 对象。另外,在 then onFulfilled 的函数当中的返回值,可以作为后续操作的参数。
catch 方法是 then(onFulfilled, onRejected) 方法当中 onRejected 函数的一个简单的写法,也就是说
可以写成 then(fn).catch(fn),相当于 then(fn).then(null, fn)。使用 catch 的写法比一般的写法更加清晰明确。
这是一个很常见的错误。很多程序员对他们代码中的promise调用十分自信,觉得代码永远不会抛出一个 error ,
也可能他们只是简单的忘了加 catch() 方法。不幸的是,不加 catch() 方法会让回调函数中抛出的异常被吞噬,
在你的控制台是看不到相应的错误的,这对调试来说是非常痛苦的。
为了避免这种糟糕的情况,我已经养成了在自己的promise调用链最后添加如下代码的习惯:
somePromise().then(function () { return aPromise(); }).then(function () { return anotherPromise(); }).catch(function(error){ console.log(error); });即使你并不打算在代码中处理异常,在代码中添加 catch() 也是一个谨慎的编程风格的体现。在某种情况下
你原先的假设出错的时候,这会让你的调试工作轻松一些。
在then方法内部,我们可以做三件事: 1.return 一个promise对象 2.return一个同步的值或者是 undefined 3.同步的 throw 一个错误
理解这三种情况之后,你就会理解promise了。
1.返回另一个promise对象
在有关promise的相关文章中,这种写法很常见,就像上文提到的构成promise链的一段代码:
getUserByName('nolan').then(function (user) { return fetch(REQUEST_URL) .then((response) => response.json()) .then((responseData) => { } }).then(function () { });2.返回一个具体的值或者是 undefined
getUserByName('nolan').then(fcuntion (user) { if (inMemoryCache[user.id]) { return inMemoryCache[user.id]; // returning a value! } return inMemoryCache[user.id]; // returning a promise }).then(function (userAccount) { // I got a user account })如果不调用return语句的话,javaScript里的函数会返回 undefined 。这也就意味着在你想返回一些值的时候,
不显式调用return会产生一些副作用。
出于上述原因,养成了一个个人习惯就是在then方法内部永远显式的调用return或者throw。我也推荐你这样做。
3.抛出一个错误
说到throw,这又体现了promise的功能强大。在用户退出的情况下,我们的代码中会采用抛出异常的方式进行处理:
getUserByName('nolan').then(function (user) { if (user.isLoggedOut()) { throw new Error('user logged out!'); // throwing a error! } if (inMemoryCache[user.id]) { return inMemoryCache[user.id]; // returning a value! } return getUserAccountById(user.id); // returning a promise! }).then(function (userAccount) { // I got a user account! }).catch(function (err) { // Boo, I got an error! });如果用户已经登出的话, catch() 会收到一个错误,如果有promise对象的状态变为rejected的话,
它还会收到一个错误。
在使用promise的时候抛出异常在开发阶段很有用,它能帮助我们定位代码中的错误。比方说,
在then函数内部调用 JSON.parse() ,如果JSON对象不合法的话,可能会抛出异常,在回调函数中,这个异常会被吞噬,但是在使用promise之后,我们就可以捕获到这个异常了。
作者:梅庆 链接:http://www.jianshu.com/p/174d9892283f 來源:简书 铁柱同学 认证博客专家 PHP 求知者 伪全栈 博主从事php开发已然五年,然入行越久越深感知识无限,学海无涯,非一时之功。故编写博客,用于自省记录,也遵循前辈们的教导,希望可以帮到更多的人。愿各位都能勇攀高峰,顺利成为大佬!奥利给!