promise解决了什么问题
JavaScript是单线程的,所以我们要用一些异步编程方案来实现异步。
回调函数就是其中的一种方案,比如说在node中写读取一个文件,使用回调函数这是没有问题的。
1 2 3 4
| fs.readFile('/etc/passwd', function (err, data) { if (err) throw err; console.log(data); });
|
但如果新的需求是必须先读取A文件,才能读取B文件
1 2 3 4 5
| fs.readFile(fileA, function (err, data) { fs.readFile(fileB, function (err, data) { }) });
|
不难想象,如果依次读取多个文件,就会出现多重嵌套。代码不是纵向发展,而是横向发展,很快就会乱成一团,无法管理。这种情况就称为”回调地狱”(callback hell)。
而promise就是为了解决这个问题而生的
什么是promise
它不是新的语法功能,而是一种新的写法,允许将回调函数的横向加载,改成纵向加载。采用Promise,连续读取多个文件,写法如下。
1 2 3 4 5 6 7 8 9 10 11
| var readFile = require('fs-readfile-promise'); readFile(fileA) .then(function(data){ console.log(data.toString()); }).then(function(){ return readFile(fileB); }).then(function(data){ console.log(data.toString()); }).catch(function(err) { console.log(err); });
|
上面代码中,我使用了 fs-readfile-promise 模块,它的作用就是返回一个 Promise 版本的 readFile 函数。Promise 提供 then 方法加载回调函数,catch方法捕捉执行过程中抛出的错误。看起来清爽多了
再来看一段伪代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| step1(function (value1) { step2(value1, function(value2) { step3(value2, function(value3) { step4(value3, function(value4) { }); }); });
(new Promise(step1)) .then(step2) .then(step3) .catch(step4);
|
promise对象,充当异步操作与回调函数之间的中介,将异步编程大大简化了。
Promise 的状态
等待(pending):初始状态。
已完成(fulfilled):意味着操作成功完成。
已失败(rejected):意味着操作失败。
Promise对象的状态只可能处于这三种之一,它的状态改变只可能是两种可能:从 Pending 变为 fulfilled 和从 Pending 变为 Rejected。一旦状态发生改变,就不会再变,这也是Promise[承诺]这个名字的由来。
promise的使用
再来看一个简单的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function getNumber(num){ return new Promise(function(resolve,reject){ if(num > 5){ resolve(num) }else{ reject('数字太小')} }) } function printData(data){ console.log('resolve'); console.log(data); } function printError(data){ console.log('reject'); console.log(data); } getNumber(4).then(printData).catch(printError); getNumber(6).then(printData).catch(printError);
|
当promise执行了resolve语句(模拟异步操作执行成功),那么promise的状态就回变为resolve,可以使用then方法,调用传入的回调函数,而回调函数的参数就是resolve中返回的数据。
相反
promise执行了reject语句(模拟异步操作执行失败),那么promise的状态就回变为reject,可以使用catch方法,调用传入的回调函数,而回调函数的参数就是reject中返回的数据。
promise的其他方法
promise.all
Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果
将两个promise对象拼成一个数组传进all方法。看有没有失败,有一个失败的,则整个all方法产生的promise对象失败。
只要有一个失败了,这个promise就失败,结果为第一个失败的结果。如果都成了,就返回全部成功的结果。
1 2 3 4 5 6 7 8 9 10 11 12 13
| const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('hello'); }, 1000); }) const p2 = new Promise((resolve, reject) => { resolve(1); }) Promise.all([p1,p2]).then( result=>console.log(result) ).catch( e=>console.log(e) )
|
将因为两个promise对象的状态都是已完成,所以返回的是数组,包含着两个promise的回调参数
在看另外一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const p1 = new Promise((resolve, reject) => { resolve('第一个任务'); }) const p2 = new Promise((resolve,reject) =>{ setTimeout(() => { reject('第二个任务') }, 1000); }) const p3 = new Promise((resolve, reject) => { setTimeout(() => { reject('第三个任务'); }, 500); }) Promise.all([p1,p2,p3]).then( result=>console.log(result) ).catch( e=>console.log(e) )
|
执行结果打印的是第三个任务,细品这段代码就能明白Promise.all干的是啥了
promise.all的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| Promise.all = function (promise) { let promises = Array.from(promise) return new Promise((resolve, reject) => { if (promises.length === 0) { resolve([]); } else { let result = []; let index = 0; for (let i = 0; i < promises.length; i++ ) { Promise.resolve(promises[i]) .then(data => { result[i] = data; if (++index === promises.length) { resolve(result); } }, err => { reject(err); return; }); } } }); }
|
Promise.race()
Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝
举个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('第一个任务'); }, 200); }) const p2 = new Promise((resolve,reject) =>{ setTimeout(() => { reject('第二个任务') }, 1000); }) const p3 = new Promise((resolve, reject) => { setTimeout(() => { resolve('第三个任务'); }, 500); }) Promise.all([p1,p2,p3]).then( result=>console.log(result) ).catch( e=>console.log(e) )
|
只要其中一个实例先改变状态,状态就跟着改变将率先改变的Promise返回值传递给回调函数,大白话:看谁快
Promise.race()实现
1 2 3 4 5 6 7 8 9 10 11 12
| var race = function(promise) { let promises = Array.from(promise) return new Promise(function(resolve, reject) { for (var i = 0; i < promises.length; i++) { Promise.resolve(promises[i]).then(data => { resolve(data); }, err => { return reject(err) }) } }) }
|
Promise.resolve()和Promise.reject()
1 2 3 4 5 6 7 8
| const p = Promise.reject('出错了');
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) { console.log(s) });
|
1 2 3
| Promise.resolve('foo')
new Promise(resolve => resolve('foo'))
|