普通网站服务器,甘肃省和城乡建设厅网站,制作视频的软件哪个好用,中软属于国企还是央企1. 引言
JavaScript 是一种单线程语言#xff0c;这意味着它一次仅能执行一个任务。为了处理异步操作#xff0c;JavaScript 提供了回调函数#xff0c;但是随着项目处理并发任务的增加#xff0c;回调地狱 (Callback Hell) 使异步代码很难维护。为此#xff0c;ES6带来了…1. 引言
JavaScript 是一种单线程语言这意味着它一次仅能执行一个任务。为了处理异步操作JavaScript 提供了回调函数但是随着项目处理并发任务的增加回调地狱 (Callback Hell) 使异步代码很难维护。为此ES6带来了Promise给了一种更清晰的异步操作模型。
2. 对Promise的理解
Promise是异步编程的一种解决方案它是一个对象可以获取异步操作的消息他的出现大大改善了异步编程的困境避免了地狱回调它比传统的解决方案回调函数和事件更合理和更强大。
所谓Promise简单说就是一个容器里面保存着某个未来才会结束的事件通常是一个异步操作的结果。从语法上说Promise 是一个对象从它可以获取异步操作的消息。Promise 提供统一的 API各种异步操作都可以用同样的方法进行处理。
1Promise的实例有三个状态:
Pending进行中Resolved已完成Rejected已拒绝 当把一件事情交给promise时它的状态就是Pending任务完成了状态就变成了Resolved、没有完成失败了就变成了Rejected。
2Promise的实例有两个过程
pending - fulfilled : Resolved已完成pending - rejectedRejected已拒绝
注意一旦从进行状态变成为其他状态就永远不能更改状态了。
2.1 Promise的特点
对象的状态不受外界影响。 promise对象代表一个异步操作有三种状态pending进行中、fulfilled已成功、rejected已失败。只有异步操作的结果可以决定当前是哪一种状态任何其他操作都无法改变这个状态这也是promise这个名字的由来——“承诺” 一旦状态改变就不会再变任何时候都可以得到这个结果。 promise对象的状态改变只有两种可能从 pending变为 fulfilled从 pending变为 rejected。这时就称为 resolved已定型。如果改变已经发生了你再对promise对象添加回调函数也会立即得到这个结果。这与事件event完全不同事件的特点是如果你错过了它再去监听是得不到结果的。
2.2 Promise的缺点
无法取消Promise一旦新建它就会立即执行无法中途取消。如果不设置回调函数Promise内部抛出的错误不会反应到外部。当处于pending状态时无法得知目前进展到哪一个阶段刚刚开始还是即将完成。
3. Promise的基本用法
3.1 创建Promise对象
Promise构造函数接受一个函数作为参数该函数的两个参数分别是 resolve和 reject。
const promise new Promise(function(resolve, reject) {// ... some codeif (/* 异步操作成功 */){resolve(value);} else {reject(error);}
});使用resolve和reject方法
function testPromise(ready) {return new Promise(function(resolve,reject){if(ready) {resolve(hello world);}else {reject(No thanks);}});
};
// 方法调用
testPromise(true).then(function(msg){console.log(msg);
},function(error){console.log(error);
});上面的代码的含义是给 testPromise方法传递一个参数返回一个promise对象如果为 true的话那么调用promise对象中的 resolve()方法并且把其中的参数传递给后面的 then第一个函数内因此打印出 “hello world”, 如果为 false的话会调用promise对象中的 reject()方法则会进入 then的第二个函数内会打印 No thanks
3.2 Promise方法
1then()
第一个回调函数是Promise对象的状态变为 resolved时调用第二个回调函数是Promise对象的状态变为 rejected时调用。其中第二个参数可以省略。
promise.then(function(value) {// success
}, function(error) {// failure
});当要写有顺序的异步事件时需要串行写
let promise new Promise((resolve,reject){ajax(first).success(function(res){resolve(res);})
})
promise.then(res{return new Promise((resovle,reject){ajax(second).success(function(res){resolve(res)})})
}).then(res{return new Promise((resovle,reject){ajax(second).success(function(res){resolve(res)})})
}).then(res{})事件没有顺序或者关系时还如何写呢可以使用 all 方法来解决。
2catch()
该方法相当于 then方法的第二个参数指向 reject的回调函数。
p.then((data) {console.log(resolved,data);
},(err) {console.log(rejected,err);}
);
// 或
p.then((data) {console.log(resolved,data);
}).catch((err) {console.log(rejected,err);
});3all()
all方法可以完成并行任务 它接收一个数组数组的每一项都是一个 promise对象。当数组中所有的 promise的状态都达到 resolved的时候all方法的状态就会变成 resolved如果有一个状态变成了 rejected那么 all方法的状态就会变成 rejected。
let promise1 new Promise((resolve,reject){setTimeout((){resolve(1);},2000)
});
let promise2 new Promise((resolve,reject){setTimeout((){resolve(2);},1000)
});
let promise3 new Promise((resolve,reject){setTimeout((){resolve(3);},3000)
});
Promise.all([promise1,promise2,promise3]).then(res{console.log(res);//结果为[1,2,3]
})4race()
race方法和 all一样接受的参数是一个每项都是 promise的数组但是与 all不同的是当最先执行完的事件执行完之后就直接返回该 promise对象的值。如果第一个 promise对象状态变成 resolved那自身的状态变成了 resolved反之第一个 promise变成 rejected那自身状态就会变成 rejected。
let promise1 new Promise((resolve,reject){setTimeout((){reject(1);},2000)
});
let promise2 new Promise((resolve,reject){setTimeout((){resolve(2);},1000)
});
let promise3 new Promise((resolve,reject){setTimeout((){resolve(3);},3000)
});
Promise.race([promise1,promise2,promise3]).then(res{console.log(res);//结果2
},rej{console.log(rej)};
)race方法有什么实际作用呢当要做一件事超过多长时间就不做了可以用这个方法来解决
Promise.race([promise1,timeOutPromise(5000)]).then(res{})5finally()
finally方法用于指定不管 Promise 对象最后状态如何都会执行的操作
promise
.then(result {···})
.catch(error {···})
.finally(() {···});4. 使用async/await代替Promise
4.1 对async/await的理解
async function testAsy(){return hello world;
}
let result testAsy();
console.log(result)async 函数返回的是一个 Promise 对象所以在最外层不能用 await 获取其返回值的情况下当然应该用原来的方式then() 链来处理这个 Promise 对象就像这样
async function testAsy(){return hello world
}
let result testAsy()
console.log(result)
result.then(v{console.log(v) // hello world
})那如果 async 函数没有返回值又该如何很容易想到它会返回 Promise.resolve(undefined)。
联想一下 Promise 的特点——无等待所以在没有 await 的情况下执行 async 函数它会立即执行返回一个 Promise 对象并且绝不会阻塞后面的语句。这和普通返回 Promise 对象的函数并无二致。
注意Promise.resolve(x) 可以看作是 new Promise(resolve resolve(x)) 的简写可以用于快速封装字面量对象或其他对象将其封装成 Promise 实例。
4.2 async/await的优势
单一的 Promise 链并不能发现 async/await 的优势但是如果需要处理由多个 Promise 组成的 then 链的时候优势就能体现出来了
假设一个业务分多个步骤完成每个步骤都是异步的而且依赖于上一个步骤的结果。仍然用 setTimeout 来模拟异步操作
/*** 传入参数 n表示这个函数执行的时间毫秒* 执行的结果是 n 200这个值将用于下一步骤*/
function takeLongTime(n) {return new Promise(resolve {setTimeout(() resolve(n 200), n);});
}
function step1(n) {console.log(step1 with ${n});return takeLongTime(n);
}
function step2(n) {console.log(step2 with ${n});return takeLongTime(n);
}
function step3(n) {console.log(step3 with ${n});return takeLongTime(n);
}现在用 Promise 方式来实现这三个步骤的处理
function doIt() {console.time(doIt);const time1 300;step1(time1).then(time2 step2(time2)).then(time3 step3(time3)).then(result {console.log(result is ${result});console.timeEnd(doIt);});
}
doIt();
// c:\var\testnode --harmony_async_await .
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
// doIt: 1507.251ms如果用 async/await 来实现呢会是这样
async function doIt() {console.time(doIt);const time1 300;const time2 await step1(time1);const time3 await step2(time2);const result await step3(time3);console.log(result is ${result});console.timeEnd(doIt);
}
doIt();4.3 async/await对比Promise的优势
代码读起来更加同步Promise虽然摆脱了回调地狱但是then的链式调⽤也会带来额外的阅读负担Promise传递中间值⾮常麻烦⽽async/await⼏乎是同步的写法⾮常优雅错误处理友好async/await可以⽤成熟的try/catchPromise的错误捕获⾮常冗余调试友好Promise的调试很差由于没有代码块你不能在⼀个返回表达式的箭头函数中设置断点如果你在⼀个.then代码块中使⽤调试器的步进(step-over)功能调试器并不会进⼊后续的.then代码块因为调试器只能跟踪同步代码的每⼀步。
4.4 async/await如何捕获异常
使用 try…catch…
async function fn(){try{let a await Promise.reject(error)}catch(error){console.log(error)}
}5. 补充Promise及其方法的手写
5.1 手写Promise
Promise 具有 pending进行中、fulfilled已成功和 rejected已失败三种状态且状态不可逆。new Promise(executor) 立即执行 executor 函数并传入 resolve 和 reject以控制 Promise 状态。
class MyPromise{constructor(executor){this.state pending;this.value undefined; // 失败的值this.error undefined; // 成功的值this.onFulfilledCallbacks []; // 存储成功回调this.onRejectedCallbacks []; // 存储失败回调const resolve (value){if(this.statepending){this.state fulfilled;this.value value;this.onFulfilledCallbacks.forEach(fnfn(value)); // 执行所有成功回调}};const reject (error){if(this.statepending){this.state rejected;this.error error;this.onRejectedCallbacks.forEach(fnfn(error));}};try{executor(resolve,reject);}catch(error){reject(error);}}
}
5.2. 手写Promise.then
在 Promise 变为 fulfilled 时调用 onFulfilled并传入结果。在 Promise 变为 rejected 时调用 onRejected并传入错误。then 必须返回一个新的 Promise支持链式调用。
Promise.prototype.then function(onFulfilled,onRejected){return new Promise((resolve,reject){if(this.statefulfilled){try {const result onFulfilled?onFulfilled(this.value):this.value;resolve(result);} catch (error) {reject(error);}}else if(this.staterejected){try {const result onRejected?onRejected(this.error):this.error;reject(result);} catch (error) {reject(error);}}else{this.onFulfilledCallbacks.push((value){try {const result onFulfilled ? onFulfilled(value) : value;resolve(result);} catch (error) {reject(error);}})this.onRejectedCallbacks.push((error) {try {const result onRejected ? onRejected(error) : error;reject(result);} catch (error) {reject(error);}});}})
}注意
Promise.then是实例方法实例方法是挂载在prototype上的只能通过实例去调用不能直接挂载在Promise上而all和race是静态方法可以直接挂载在Promise上
5.3 手写Promise.all
1核心思路
接收一个 Promise 实例的数组或具有 Iterator 接口的对象作为参数这个方法返回一个新的 promise 对象遍历传入的参数用Promise.resolve()将参数包一层使其变成一个promise对象参数所有回调成功才是成功返回值数组与参数顺序一致参数数组其中一个失败则触发失败状态第一个触发失败的 Promise 错误信息作为 Promise.all 的错误信息。
2代码实现
一般来说Promise.all 用来处理多个并发请求也是为了页面数据构造的方便将一个页面所用到的在不同接口的数据一起请求过来不过如果其中一个接口失败了多个请求也就失败了页面可能啥也出不来这就看当前页面的耦合程度了
function promiseAll(promises) {return new Promise(function(resolve, reject) {if(!Array.isArray(promises)){throw new TypeError(argument must be a array)}var resolvedCounter 0;var promiseNum promises.length;var resolvedResult [];for (let i 0; i promiseNum; i) {Promise.resolve(promises[i]).then(value{resolvedCounter;resolvedResult[i] value;if (resolvedCounter promiseNum) {return resolve(resolvedResult)}},error{return reject(error)})}})
}
// test
let p1 new Promise(function (resolve, reject) {setTimeout(function () {resolve(1)}, 1000)
})
let p2 new Promise(function (resolve, reject) {setTimeout(function () {resolve(2)}, 2000)
})
let p3 new Promise(function (resolve, reject) {setTimeout(function () {resolve(3)}, 3000)
})
promiseAll([p3, p1, p2]).then(res {console.log(res) // [3, 1, 2]
})5.3 手写Promise.race
该方法的参数是 Promise 实例数组, 然后其 then 注册的回调方法是数组中的某一个 Promise 的状态变为 fulfilled 的时候就执行. 因为 Promise 的状态只能改变一次, 那么我们只需要把 Promise.race 中产生的 Promise 对象的 resolve 方法, 注入到数组中的每一个 Promise 实例中的回调函数中即可。
Promise.race function (args) {return new Promise((resolve, reject){for(let i 0; i args.length; i){Promise.resolve(args[i]).then(resolve,reject)}})
}