如果需要執(zhí)行一段異步代碼塘幅,js有以下幾種方案:
回調(diào)函數(shù)
setTimeout(function(){
setTimeout(function(){
setTimeout(function(){
......
})
})
})
有一些問(wèn)題:
- 層層嵌套,代碼難看饥追,不優(yōu)雅
- try{}cattch()難捕獲異常
- 不能return
事件監(jiān)聽(tīng)
發(fā)布/訂閱
Promise
- Promise是一個(gè)類(lèi)鬼吵,類(lèi)的構(gòu)造函數(shù)接收一個(gè)task任務(wù)函數(shù)边篮,該函數(shù)接收兩個(gè)JavaScript引擎提供的兩個(gè)參數(shù)resolve, reject
- 立即執(zhí)行傳入的task函數(shù)
- 如果操作(一般是異步操作)成功幢码,調(diào)用resolve方法,并把異步的結(jié)果傳入,如果傳入的是一個(gè)promise尖飞,則自己的狀態(tài)無(wú)效症副,傳入的promise的狀態(tài)決定了該promise的狀態(tài)店雅,如果傳入的不是一個(gè)promise,resolve方法會(huì)把等待狀態(tài)變成成功狀態(tài)(pending -> resolved)贞铣;如果操作(一般是異步操作)失敗闹啦,調(diào)用reject方法,并把異步的結(jié)果傳入,reject方法會(huì)把等待狀態(tài)變成失敗狀態(tài)(pending -> rejected)辕坝。
- then方法指定回調(diào)函數(shù)窍奋,接收兩個(gè)回調(diào)函數(shù)resolve, reject。當(dāng)狀態(tài)從pengding->resolved時(shí)酱畅,執(zhí)行resolve琳袄,如果resolve的結(jié)果是一個(gè)promise,這是后面一個(gè)回調(diào)函數(shù)會(huì)等待該promise完成后執(zhí)行纺酸;當(dāng)狀態(tài)從pengding->rejected時(shí)窖逗,執(zhí)行reject。
- then方法返回一個(gè)新的Promise實(shí)例(不是原來(lái)的Promise)餐蔬,因此可以鏈?zhǔn)秸{(diào)用碎紊。
下面是模擬ES6,自己代碼代碼實(shí)現(xiàn)Promise:
function Promise(task) {
var t = this;
t.value;
t.statues = "pending";
t.resolveCallbacks = [];
t.rejectCallbacks = [];
function resolve(value){
if(value instanceof Promise){
value.then(resolve, reject);
}else{
t.statues = "fufilled";
t.value = value;
t.resolveCallbacks.forEach(item=>item(value));
}
}
function reject(value){
if(value instanceof Promise){
value.then(resolve, reject);
}else{
t.statues = "rejected";
t.value = value;
t.rejectCallbacks.forEach(item=>item(value));
}
}
try{
task(resolve, reject);
}catch(e){
reject(e);
}
}
function resolvePromise(x, resolve, reject){
if(x instanceof Promise){
if(x.statues == "fufilled"){
resolve(x.value);
}else if(x.statues == "rejected"){
reject(x.value);
}else{
x.then(function (y) {
resolvePromise(y, resolve, reject);
}, reject)
}
}else if(x != null && (typeof x == "object" || typeof x == "function")){
let then = x.then;
if(typeof then == "function"){
x.then.call(x, function (y2) {
resolvePromise(y2, resolve, reject);
}, reject)
}
}else{
resolve(x);
}
}
Promise.prototype = {
then: function (resolveCall, rejectCall) {
let t = this;
let promise2;
resolveCall = typeof resolveCall == "function" ? resolveCall : function () {
return t.value;
}
rejectCall = typeof rejectCall == "function" ? rejectCall : function () {
return t.value;
}
if(t.statues == "fufilled"){
promise2 = new Promise(function (resolve, reject) {
let x = resolveCall(t.value);
resolvePromise(x, resolve, reject);
})
}else if(t.statues == "rejected"){
promise2 = new Promise(function (resolve, reject) {
let x = rejectCall(t.value);
resolvePromise(x, resolve, reject);
})
}else if(t.statues == "pending"){
promise2 = new Promise(function (resolve, reject) {
t.resolveCallbacks.push(function () {
resolvePromise(resolveCall(t.value), resolve, reject);
});
t.rejectCallbacks.push(function () {
resolvePromise(rejectCall(t.value), resolve, reject);
});
})
}
return promise2;
}
}
// 快捷方法
Promise.resolve = function (value) {
return new Promise(function(resolve, reject){
resolve(value);
});
}
Promise.reject = function (value) {
return new Promise(function(resolve, reject){
reject(value);
});
}
// 所有成功resolve樊诺,否則reject
Promise.all = function (promises) {
if(!Array.isArray(promises)){
return TypeError("參數(shù)類(lèi)型錯(cuò)誤");
}
return new Promise(function (resolve, reject) {
let result = [];
let index = 0;
if(promises.length > 0){
for(let i = 0; i<promises.length; i++){
promises[i].then(function (value) {
index++;
result[i] = value;
if(index == promises.length){
resolve(result);
}
}, function (value) {
reject(value);
})
}
}
})
}
// 競(jìng)速仗考,誰(shuí)跑得快先返回誰(shuí)
Promise.race = function (promises) {
if(!Array.isArray(promises)){
return TypeError("參數(shù)類(lèi)型錯(cuò)誤");
}
return new Promise(function (resolve, reject) {
if(promises.length > 0){
for(let i = 0; i<promises.length; i++){
promises[i].then(function (value) {
resolve(value);
}, function (value) {
reject(value);
})
}
}
})
}
module.exports = Promise;
Generator
- Generator可以暫停函數(shù)執(zhí)行,返回任意表達(dá)式的值词爬。
- Generator函數(shù)有兩個(gè)特征秃嗜,一是,function關(guān)鍵字和函數(shù)名之間有個(gè)*號(hào)缸夹,二是痪寻,函數(shù)體內(nèi)部使用yield表達(dá)式。
- 調(diào)用Generator并不執(zhí)行虽惭,返回的是遍歷器對(duì)象(Iterator)橡类。調(diào)用Iterator的next方法后,函數(shù)內(nèi)部指針從函數(shù)頭部或者上一次停下來(lái)的地方開(kāi)始執(zhí)行芽唇,直到遇到下一個(gè)yield表達(dá)式顾画。
- 遇到y(tǒng)ield表達(dá)式,next函數(shù)返回一個(gè)對(duì)象形如{value: , done: false|true}形式匆笤,表達(dá)式的value值是yield后面的表達(dá)式值或者最后的return值研侣,如果沒(méi)有return,函數(shù)執(zhí)行完炮捧,value值就是undefined庶诡。yield表達(dá)式的返回值是next函數(shù)的傳入值或者undefined。
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
for...of循環(huán)
for...of循環(huán)自動(dòng)遍歷Generator函數(shù)生成的Iterator對(duì)象咆课,不需要使用next()末誓。
另外扯俱,也可以使用拓展運(yùn)算符(...)、解構(gòu)賦值喇澡,Array.from方法內(nèi)部調(diào)用的迅栅,都是遍歷器接口。因此:
function* numbers () {
yield 1
yield 2
return 3
yield 4
}
// 擴(kuò)展運(yùn)算符
[...numbers()] // [1, 2]
// Array.from 方法
Array.from(numbers()) // [1, 2]
// 解構(gòu)賦值
let [x, y] = numbers();
x // 1
y // 2
// for...of 循環(huán)
for (let n of numbers()) {
console.log(n)
}
// 1
// 2
async
- async函數(shù)是Generator函數(shù)的語(yǔ)法糖晴玖。
- async函數(shù)返回一個(gè)Promise對(duì)象
參考: