JavaScript所有代碼都是單線程執(zhí)行的,所以avaScript的所有網(wǎng)絡(luò)操作援奢,瀏覽器事件蓖谢,都必須是異步執(zhí)行根吁。異步執(zhí)行可以用回調(diào)函數(shù)實(shí)現(xiàn)
Js 的異步提高了程序的執(zhí)行效率,同時(shí)也減少了程序的可讀性端盆。
異步操作會在將來的某個(gè)時(shí)間點(diǎn)觸發(fā)一個(gè)函數(shù)調(diào)用
AJAX就是典型的異步操作
es6 promise
Promise有各種開源實(shí)現(xiàn)怀骤,在ES6中被統(tǒng)一規(guī)范费封,由瀏覽器直接支持。
promiseA+規(guī)范中文 英文
// promise用法
new Promise(function (resolve, reject) {
log('start new Promise...');
var timeOut = Math.random() * 2;
log('set timeout to: ' + timeOut + ' seconds.');
setTimeout(function () {
if (timeOut < 1) {
log('call resolve()...');
resolve('200 OK');
}
else {
log('call reject()...');
reject('timeout in ' + timeOut + ' seconds.');
}
}, timeOut * 1000);
}).then(function (r) {
log('Done: ' + r);
}).catch(function (reason) {
log('Failed: ' + reason);
});
// promise all
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
// 同時(shí)執(zhí)行p1和p2蒋伦,并在它們都完成后執(zhí)行then:
Promise.all([p1, p2]).then(function (results) {
console.log(results); // 獲得一個(gè)Array: ['P1', 'P2']
});
// promise race
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
console.log(result); // 'P1'
});
同步回調(diào)和異步回調(diào)
// 同步回調(diào)
var arr = [1,2,3];
arr.forEach(function (x) {
console.log('first');
});
console.log('last');
// first first first
// last
// 異步回調(diào)
setTimeout(function () {
console.log('last')
}, 1000);
console.log('first');
// first
// last
實(shí)現(xiàn)一個(gè)簡單的promise
promiseA+規(guī)范的內(nèi)容
- Promise 本質(zhì)是一個(gè)狀態(tài)機(jī)弓摘。每個(gè) promise 只能是 3 種狀態(tài)中的一種:pending、fulfilled 或 rejected痕届。狀態(tài)轉(zhuǎn)變只能是 pending -> fulfilled 或者 pending -> rejected衣盾。狀態(tài)轉(zhuǎn)變不可逆。
- then 方法可以被同一個(gè) promise 調(diào)用多次爷抓。
- then 方法必須返回一個(gè) promise势决。規(guī)范里沒有明確說明返回一個(gè)新的 promise 還是復(fù)用老的 promise(即 return this),大多數(shù)實(shí)現(xiàn)都是返回一個(gè)新的 promise蓝撇,而且復(fù)用老的 promise 可能改變內(nèi)部狀態(tài)果复,這與規(guī)范也是相違背的。
創(chuàng)建MyPromise類并根據(jù)規(guī)范初始化狀態(tài)
promise的鏈?zhǔn)秸{(diào)用
實(shí)現(xiàn)一個(gè)
const log = console.log;
// 1.創(chuàng)建類
class MyPromise {
constructor(executor) {
// 2.等待狀態(tài)
this.status = "pending";
// 初始化data
this.data = undefined;
// 初始化reason
this.reason = undefined;
// 3.執(zhí)行函數(shù)executor渤昌,定義我們的resolve虽抄,reject回調(diào)
let resolve = (data) => {
// log('resolve executor: '+ data);
// 為了防止多次改變狀態(tài)
if(this.status === 'pending') {
this.status = 'resolved'
this.data = data;
this.onFulFilledCallbacks.forEach((fn) => {
fn()
})
}
}
let reject = (reason) => {
// 為了防止多次改變狀態(tài)
if(this.status === 'pending') {
this.status = 'rejected'
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => {
fn()
})
}
}
// 5.儲存fulfilled的回調(diào)
this.onFulFilledCallbacks = [];
this.onRejectedCallbacks = [];
try {
executor(resolve, reject);
} catch(err) {
reject(err);
}
}
// 4. MyPromise原型上的then方法
then (onFulFilled, onRejected) {
// log('then executor');
// 處理無參數(shù)時(shí)的問題(值穿透)
onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : function(value) {return value};
// 拋出錯(cuò)誤直接丟到下一個(gè)then中
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
let promise2;
// 由于 promise.then 執(zhí)行的時(shí)候promise對象已經(jīng)是確定狀態(tài),從程序上說對回調(diào)函數(shù)進(jìn)行同步調(diào)用也是行得通的独柑。
// 但是即使在調(diào)用 promise.then 注冊回調(diào)函數(shù)的時(shí)候promise對象已經(jīng)是確定的狀態(tài)迈窟,
// Promise也會以異步的方式調(diào)用該回調(diào)函數(shù),這是在Promise設(shè)計(jì)上的規(guī)定方針忌栅。
if(this.status === 'resolved'){
promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFulFilled(this.data);
// log('onFulFilled return value : '+x)
resolvePromsie(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
}, 0);
})
}
if(this.status === 'rejected'){
promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
// If either onFulfilled or onRejected returns a value x,
// run the Promise Resolution Procedure [[Resolve]](promise2, x).
let x = onRejected(this.reason);
resolvePromsie(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
}, 0)
})
}
if(this.status === 'pending'){
// 這里要做一件很有意思的事车酣。。索绪。湖员。
promise2 = new MyPromise((resolve, reject) => {
// 將回調(diào)放在數(shù)組中等待調(diào)用
this.onFulFilledCallbacks.push(() => {
process.nextTick(() => {
try {
let x = onFulFilled(this.data);
resolvePromsie(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
})
})
this.onRejectedCallbacks.push(() => {
process.nextTick(() => {
try {
let x = onRejected(this.reason);
resolvePromsie(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
});
})
})
}
return promise2;
}
catch(onRejected){
if(this.status === 'rejected'){
promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
// If either onFulfilled or onRejected returns a value x,
// run the Promise Resolution Procedure [[Resolve]](promise2, x).
let x = onRejected(this.reason);
resolvePromsie(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
}, 0)
})
}
}
}
function resolvePromsie(promise, x, resolve, reject){
// 規(guī)范說先promise是不是來自同一個(gè)對象,并返回一個(gè)理由
// If promise and x refer to the same object, reject promise with a TypeError as the reason.
try {
// If promise and x refer to the same object, reject promise with a TypeError as the reason.
// 如果是相同引用就拋出錯(cuò)誤
// if(promise === x) return reject(new TypeError('不能循環(huán)引用'));
// if x is an object or function,
// 如果是對象瑞驱,或是函數(shù)娘摔,我們就要看then是不是函數(shù)
if(x != null && (typeof x === 'object' || typeof x === 'function')){
// Let then be x.then
let then = x.then;
if(typeof then === 'function'){
// If then is a function, call it with x as this,
// first argument resolvePromise, and second argument rejectPromise, where:
// 執(zhí)行then函數(shù),拿到返回值
then.call(x, y => {
resolvePromsie(promise, y, resolve, reject);
}, (err) => {
reject(err);
})
}else{
resolve(x);
}
}else{
resolve(x);
}
} catch (err) {
reject(err)
}
}
var mypromise = new MyPromise((resolve,reject) => {
resolve('第一個(gè)異步任務(wù) OK');
// let timeOut = Math.random()*2;
// setTimeout(function () {
// if (timeOut < 1) {
// resolve('第一個(gè)異步任務(wù) OK');
// }
// else {
// reject('第一個(gè)異步任務(wù) reject');
// }
// }, timeOut * 1000);
})
var mypromise2 = new MyPromise((resolve,reject) => {
resolve('第二個(gè)異步任務(wù)');
})
// 測試
mypromise.then(function(data){
log(data)
return mypromise2;
},function(error){
log(error)
return mypromise2;
}).then().then().then(function(rd){
log(rd);
return 'test'
}).then(function(val){
log(val);
}).then(function(val){
log(val)
});
log('A'+111)
setTimeout(()=>{
log('setTimeout 0ms')
})
setTimeout(()=>{
log('setTimeout 10ms')
},10)