promise
2018-10-17 本文已影响5人
FFriday
JavaScript所有代码都是单线程执行的,所以avaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现
Js 的异步提高了程序的执行效率,同时也减少了程序的可读性。
回调陷阱
异步操作会在将来的某个时间点触发一个函数调用
AJAX就是典型的异步操作
es6 promise
Promise有各种开源实现,在ES6中被统一规范,由浏览器直接支持。
promiseA+规范中文 英文
// 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');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
console.log(results); // 获得一个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'
});
同步回调和异步回调
// 同步回调
var arr = [1,2,3];
arr.forEach(function (x) {
console.log('first');
});
console.log('last');
// first first first
// last
// 异步回调
setTimeout(function () {
console.log('last')
}, 1000);
console.log('first');
// first
// last
实现一个简单的promise
promiseA+规范的内容
- Promise 本质是一个状态机。每个 promise 只能是 3 种状态中的一种:pending、fulfilled 或 rejected。状态转变只能是 pending -> fulfilled 或者 pending -> rejected。状态转变不可逆。
- then 方法可以被同一个 promise 调用多次。
- then 方法必须返回一个 promise。规范里没有明确说明返回一个新的 promise 还是复用老的 promise(即 return this),大多数实现都是返回一个新的 promise,而且复用老的 promise 可能改变内部状态,这与规范也是相违背的。
创建MyPromise类并根据规范初始化状态
image.png
promise的链式调用
image.png
实现一个
const log = console.log;
// 1.创建类
class MyPromise {
constructor(executor) {
// 2.等待状态
this.status = "pending";
// 初始化data
this.data = undefined;
// 初始化reason
this.reason = undefined;
// 3.执行函数executor,定义我们的resolve,reject回调
let resolve = (data) => {
// log('resolve executor: '+ data);
// 为了防止多次改变状态
if(this.status === 'pending') {
this.status = 'resolved'
this.data = data;
this.onFulFilledCallbacks.forEach((fn) => {
fn()
})
}
}
let reject = (reason) => {
// 为了防止多次改变状态
if(this.status === 'pending') {
this.status = 'rejected'
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => {
fn()
})
}
}
// 5.储存fulfilled的回调
this.onFulFilledCallbacks = [];
this.onRejectedCallbacks = [];
try {
executor(resolve, reject);
} catch(err) {
reject(err);
}
}
// 4. MyPromise原型上的then方法
then (onFulFilled, onRejected) {
// log('then executor');
// 处理无参数时的问题(值穿透)
onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : function(value) {return value};
// 抛出错误直接丢到下一个then中
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
let promise2;
// 由于 promise.then 执行的时候promise对象已经是确定状态,从程序上说对回调函数进行同步调用也是行得通的。
// 但是即使在调用 promise.then 注册回调函数的时候promise对象已经是确定的状态,
// Promise也会以异步的方式调用该回调函数,这是在Promise设计上的规定方针。
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) => {
// 将回调放在数组中等待调用
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){
// 规范说先promise是不是来自同一个对象,并返回一个理由
// 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.
// 如果是相同引用就抛出错误
// if(promise === x) return reject(new TypeError('不能循环引用'));
// if x is an object or function,
// 如果是对象,或是函数,我们就要看then是不是函数
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:
// 执行then函数,拿到返回值
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('第一个异步任务 OK');
// let timeOut = Math.random()*2;
// setTimeout(function () {
// if (timeOut < 1) {
// resolve('第一个异步任务 OK');
// }
// else {
// reject('第一个异步任务 reject');
// }
// }, timeOut * 1000);
})
var mypromise2 = new MyPromise((resolve,reject) => {
resolve('第二个异步任务');
})
// 测试
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)