Promise 是一个构造函数,它自身拥有all、reject、resolve这几个眼熟的方法,
原型上有then、catch等同样熟悉的方法。
所以,在开始一个Promise的时候,先new一个吧:
let p = new Promise((resolve, reject)=> { setTimeout(()=> { resolve('执行完成') }, 1000) })
Promise是一个构造函数,接受一个回调函数作为参数,回调函数的参数是resolve、reject。分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。其实这里用“成功”和“失败”来描述并不准确,按照标准来讲,resolve是将Promise的状态置为fullfiled,reject是将Promise的状态置为rejected。不过在我们开始阶段可以先这么理解,后面再细究概念。
在上面的代码中,我们执行了一个异步操作,也就是setTimeout,1秒后,输出“执行完成”,并且调用resolve方法。
行代码,会在2秒后输出“执行完成”。注意!我只是new了一个对象,并没有调用它,我们传进去的函数就已经执行了,这是需要注意的一个细节。所以我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数,如:
function runAsync() { let p = new Promise((resolve, reject)=> { setTimeout(()=> { console.log('执行完成'); resolve(123); }, 2000) }) return p; } runAsync()
这时候你应该有个疑问:包装这么一个函数有什么作用?
在我们包装好的函数最后,会return出Promise对象 p
,也就是说,执行这个函数我们得到了一个Promise对象。Promise对象上有then、catch方法,这这个时候就可以用到它们了,看下面的代码:
runAsync().then((result)=> { console.log(result); // 做其他操作 })
在runAsync()的返回上直接调用then方法,then接收一个参数,是函数,并且会拿到我们在runAsync中调用resolve时传的的参数。运行这段代码,会在2秒后输出“执行完成”,紧接着输出 “123” 。
这个时候你可能已经明白了,原来then方法和我们平时写的回调函数是一个道理,那我们直接写回调函数就行了啊,为什么还要写Promise呢。
确实在一些简单的场景下,回调函数已经够用,但是在现在前端的大环境和大发展的状态下,经常会出现回调地狱的情况,你可能需要在回调函数中,再继续回调函数,这样下去就会很恐怖了。Promise就给我们带来了更好的解决办法。
Promise的优势在于,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作。
看下面一个例子,多层回调时是怎么使用的:
function runAsync1() { var p = new Promise(function(resolve, reject) { setTimeout(function(){ console.log('执行完成1'); resolve(1); }, 2000) }) return p; } function runAsync2() { var p = new Promise(function(resolve, reject) { setTimeout(function(){ console.log('执行完成2'); resolve(2); }, 2000) }) return p; } function runAsync3() { var p = new Promise(function(resolve, reject) { setTimeout(function(){ console.log('执行完成3'); resolve(3); }, 2000) }) return p; } runAsync1().then(function(data) { console.log(data); return runAsync2(); }).then(function(data) { console.log(data); return runAsync3(); }).then(function(data) { console.log(data); return '直接返回数据' }).then(function(data) { console.log(data); })
在链式操作中,当一个Promise结束后,可以继续返回一个新的Promise,在下一个then方法中继续接收其返回的resolve。
在then方法中,你也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了,看上面例子的最后一个。
说完resolve ,就不得不说reject,它们都是Promise自身的方法,简单来说,reject方法返回的是错误信息,将Promise的状态置为rejected,上面已经说过。
我们下面来看看具体的实现:
function getNumber() { var p = new Promise(function(resolve, reject) { setTimeout(function(){ var num = Math.ceil(Math.random()*10); if(num<5) { resolve(num) } else { reject('数字太大了') } }, 2000) }); return p; } getNumber() .then( function(data) { console.log('resolve') console.log(data); }, function(reason) { console.log('reject') console.log(reason); } )
我们创建一个随机1-10的数,判断大小,返回不同的resolve和reject。
首先我们可以在then方法中第二个参数来接收reject返回值。如果是大于5我们就会打印出数字太大了。
但是很多人之前都有接触过Promise,知道还有一个catch的链式操作方法。它也可以用来接收reject返回的信息。来看下面的代码:
getNumber() .then(function(data) { console.log('resolve'); console.log(data); }) .catch(function(reason) { console.log('reject'); console.log(reason); })
如果数字大于5就会打印 reject 6
。
如果你在then
中出现了错误,也不会中断程序的运行,会在catch
中打印出错误信息:
getNumber() .then(function(data) { console.log('resolve'); console.log(data); console.log(somedata); }) .catch(function(reason) { console.log('reject'); console.log(reason); // ReferenceError: somedata is not defined })