ES6中的Promise

什么是Promise
Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。
创建Promise
Promise 对象是由关键字 new 及其构造函数来创建的。该构造函数会把一个叫做“处理器函数”(executor function)的函数作为它的参数。这个“处理器函数”接受两个函数——resolve 和 reject ——作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve 函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject 函数。

const myFirstPromise = new Promise((resolve, reject) => {
  // ?做一些异步操作,最终会调用下面两者之一:
  //
  //   resolve(someValue); // fulfilled
  // ?或
  //   reject("failure reason"); // rejected
});

想要某个函数拥有promise功能,只需让其返回一个promise即可。

function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
};

为什么需要 promise ?
Javascript 是⼀⻔单线程语⾔,所以早期我们解决异步的场景时,⼤部分情况都是通过回调函数来进
⾏。
例如在浏览器中发送 ajax 请求,就是常⻅的⼀个异步场景,发送请求后,⼀段时间服务端响应之后我们
才能拿到结果。如果我们希望在异步结束之后执⾏某个操作,就只能通过回调函数这样的⽅式进⾏操

var dynamicFunc = function(cb) {
setTimeout(function() {
 cb();
 }, 1000);
}
dynamicFunc(function() {console.log(123)});

例如上⾯这个例⼦,这⾥的 dynamicFunc 就是⼀个异步的函数,⾥⾯执⾏的 setTimeout 会在 1s 之后调
⽤传⼊的 cb 函数。按照上⾯的调⽤⽅式,最终 1s 之后,会打印 123 这个结果。
同样的,如果后续还有内容需要在异步函数结束时输出的话,就需要多个异步函数进⾏嵌套,⾮常不利
于后续的维护。

setTimeout(function() {
 console.log(123);
 setTimeout(function() {
 console.log(321);
 // ...
 }, 2000);
}, 1000);

Promise 基础

function promise1() {
 return new Promise(function(resolve, reject) {
 // 定义异步的内容
 setTimeout(function() {
 console.log('1s 后输出');
 // 输出完成后,调⽤函数传⼊的 resolve 函数,将该 promise 实例标记为已完成,当前 promise 串
⾏继续执⾏
 resolve();
 }, 1000);
 }); 
}
function promise2() {
 return new Promise(function(resolve) {
 setTimeout(function() {
 console.log('2s 后输出');
 resolve();
 }, 2000);
 }); 
}

上⾯的两个 promise 实例,串联起来即可写为:

promise1().then(function() { return promise2(); }); 也可以简写为 promise1().then(promise2);

浏览器中执⾏之后,即可看到,1s 之后出现 1s 后输出 字样,再经过 2s 出现 2s 后输出 字样。在这个例⼦中我们能看到。当前 promise 如果状态变为已完成(执⾏了 resolve ⽅法),那么就会去执⾏ then ⽅法中的下⼀个 promise 函数。
同样的,如果我们的 promise 变为已拒绝状态(执⾏了 reject ⽅法),那么就会进⼊后续的异常处理函数中。

function promise3() {
 return new Promise(function(resolve, reject) {
 var random = Math.random() * 10; // 随机⼀个 1 - 10 的数字
 setTimeout(function() {
 if (random >= 5) {
 resolve(random);
 } else {
 reject(random);
 } 
 }, 1000);
 });
}
var onResolve = function(val) {
 console.log('已完成:输出的数字是', val);
};
var onReject = function(val) {
 console.log('已拒绝:输出的数字是', val);
}
// promise 的 then 也可以接受两个函数,第⼀个参数为 resolve 后执⾏,第⼆个函数为 reject 后执⾏
promise3().then(onResolve, onReject);
// 也可以通过 .catch ⽅法拦截状态变为已拒绝时的 promise
promise3().catch(onReject).then(onResolve);
// 也可以通过 try catch 进⾏拦截状态变为已拒绝的 promise
try {
 promise3().then(onResolve);
} catch (e) {
 onReject(e);
}

这个例⼦使⽤了三种⽅式拦截最终变为「已拒绝」状态的 promise,分别是使⽤ then 的第⼆个参数,使⽤ .catch ⽅法捕获前⽅ promise 抛出的异常,使⽤ try catch 拦截代码块中 promise 抛出的异常同时我们还可以发现,在改变 promise 状态时调⽤ resolve 和 reject 函数的时候,也可以给下⼀步 then中执⾏的函数传递参数。这个例⼦中我们把随机⽣成的数字传给了 resolve 和 reject 函数,我们也就能在then 中执⾏函数的时候拿到这个值。

如何封装异步操作为 promise
发送 ajax 请求也可以进⾏封装:

function ajax(url, success, fail) {
 var client = new XMLHttpRequest();
 client.open("GET", url);
 client.onreadystatechange = function() {
 if (this.readyState !== 4) {
 return;
 }
 if (this.status === 200) {
 success(this.response);
 } else {
 fail(new Error(this.statusText));
 }
 };
 client.send();
};
ajax('/ajax.json', function() {console.log('成功')}, function() {console.log('失败')});

我们可以看到,调⽤ ajax ⽅法时需要传⼊success 和 fail 的回调函数进⾏调⽤。我们可以不传⼊回调函数,通过封装 promise 的⽅式,在原来的执⾏回调函数的地⽅更改当前 promise 的状态,就可以通过链式调⽤。

function ajaxAsync(url) {
 return new Promise(function(resolve, reject){
 var client = new XMLHttpRequest();
 client.open("GET", url);
 client.onreadystatechange = function() {
 if (this.readyState !== 4) {
 return;
 }
 if (this.status === 200) {
 resolve(this.response);
 } else {
 reject(new Error(this.statusText));
 }
 };
 client.send();
 });
};
ajax('/ajax.json')
 .catch(function() {
 console.log('失败')})
 .then(function() {
 console.log('成功');
 })

如果你看完上面内容,恭喜你基础部分已经完成。已经可以进行项目实战了。

promise 规范解读

  • promise 的状态
    ⼀个 Promise 的当前状态必须为以下三种状态中的⼀种:等待态(Pending)、已完成(Fulfilled)和已拒绝(Rejected)。
  • 必须有⼀个 then ⽅法
    ⼀个 promise 必须提供⼀个 then ⽅法以访问其当前值和原因。
    promise 的 then ⽅法接受两个参数: promise.then(onFulfilled, onRejected) 他们都是可选参数,同时他
    们都是函数,如果 onFulfilled 或 onRejected 不是函数,则需要忽略他们。

Promise 构造函数上的静态⽅法

  • Promise.resolve
    返回⼀个 promise 实例,并将它的状态设置为已完成,同时将他的结果作为传⼊ promise 实例的值
var promise = Promise.resolve(123);
promise
 .then(function(val) {
 console.log('已完成', val);
 });
// 已完成 123

同样的, Promise.resolve 的参数也可以处理对象,函数等内容,处理⽅式和上⾯规范中介绍的相同。

  • Promise.reject
    返回⼀个 promise 实例,并将它的状态设置为已拒绝,同时也将他的结果作为原因传⼊ onRejected 函数
var promise = Promise.reject(123);
promise
 .then(null, function(val) {
 console.log('已拒绝', val);
 });
// 已拒绝 123
  • Promise.all
    返回⼀个 promise 实例,接受⼀个数组,⾥⾯含有多个 promise 实例,当所有 promise 实例都成为已完
    成状态时,进⼊已完成状态,否则进⼊已拒绝状态。
var promise1 = function() {
 return new Promise(function(resolve) {
 setTimeout(function() {
 console.log(1);
 resolve();
 }, 1000)
  });
}
var promise2 = function() {
 return new Promise(function(resolve) {
 setTimeout(function() {
 console.log(2);
 resolve();
 }, 2000);
 });
}
Promise.all([promise1(), promise2()])
 .then(function() {
 console.log('全部 promise 均已完成');
 });

注意,此时多个 promise 是同时进⾏的,也就是在上⾯这个例⼦中,等待 1s 打印 1 之后,再等待 1s 就
会打印 2 和「全部 promise 均已完成」

内部原理

/**
 * promise的内部 原理
 * @param {promise的数组} params 
 * @returns 返回结果成功  或失败
 */
const promiseall=(params)=>{
    return new Promise((resolve,reject)=>{
       let result=[];
       for (let index = 0; index < params.length; index++) {
           Promise.resolve(params[index]).then((res)=>{
            result.push(res)
            if (index===params.length-1) {
                resolve(result)
            }
           },(error)=>{ reject(error)})
           
       }
    })
}
promiseall([promise1,promise2]).then(res=>{console.log("数据是",res)},error=>{console.log(error);})
  • Promise.race
    返回⼀个 promise 实例,接受⼀个数组,⾥⾯含有多个 promise 实例,当有⼀个 promise 实例状态改变时,就进⼊该状态且不可改变。这⾥所有的 promise 实例为竞争关系,只选择第⼀个进⼊改变状态的promise 的值
    内部原理
    在这里插入图片描述
var promise1 = function() {
 return new Promise(function(resolve) {
 setTimeout(function() {
 console.log(1);
 resolve(1);
 }, 1000)
 });
}
var promise2 = function() {
 return new Promise(function(resolve) {
 setTimeout(function() {
 console.log(2);
 resolve(2);
 }, 2000);
 });
}
Promise.race([promise1(), promise2()])
 .then(function(val) {
 console.log('有⼀个 promise 状态已经改变', val);
 });

generator / async await 简介
1.async函数
函数的返回值为promise对象,promise对象的结果由async函数执行的返回值值决定
2.await函数
await右侧的表达式一般为promise对象,如果右侧是promise对象,则返回promise成功的值,如果为普通值就直接返回。
3.await必须写在async函数中,但async函数中可以没有await,如果await的promise失败了就会抛出异常,可以用try…catch来捕获。

function promise1() {
 return new Promise(function(resolve) {
 setTimeout(function() {
 console.log(1);
 resolve();
 }, 1000)
 });
}
function promise2() {
 return new Promise(function(resolve) {
 setTimeout(function() {
 console.log(2);
 resolve();
 }, 2000);
 });
}
// 使⽤ generator 函数
function* gen() {
 yield promise1();
 yield promise2();
}
var g = gen();
g.next();
g.next();
// 使⽤ async/await 函数` (async function() {
 try {
 await promise1();
 await promise2();
 console.log('已完成');
 } catch (e) {
 console.log(e);
 console.log('已拒绝');
 }
}());
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值