首先一提到Promise 我们首先想到的就是异步编程。异步编程在JavaScript中我们最早接触的是callback形式的异步,callback形式的异步编程最大的特点就是地狱式回调嵌套,一旦嵌套次数过多,就很容易使我们的代码难以理解和维护。而Permise的出现是为了更好的解决JavaScript中异步编程的问题。
js中异步的方式有哪些?
1.回调函数callback形式:
$.get(URL,function(res) {
if(res) {
$.get(URL,function(res) {
if(res) {
$.get(URL,function(res) {
if(res) {
}
});
}
});
}
});
复制代码
这就是传说中的回调地狱!!!
复制代码
2.ES6 Promise 对象:
let p = new Promise(function (resolve, reject) {
resolve('第一次成功了')
});
p.then(function (res) { //第一次promise的结果
console.log(res)
let next_p = new Promise(function (resolve, reject) {
reject('第二次失败功了')
});
return next_p
}, function (err) {
console.log(err)
}).then(function (res) { //第二次permise的结果
console.log(res)
}, function (err) {
console.log(err)
})
复制代码
上面是简单的一个 es6 promise对象的用法 代码结构上来看,并没有像 callback回调那样出现回调地狱的形式,而是常见的链式调用,(如jquery)。
复制代码
3.ES6 Generator 函数 :
Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态.
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,
可以依次遍历 Generator 函数内部的每一个状态。
形式上,Generator 函数是一个普通函数,但是有两个特征:
一是,function关键字与函数名之间有一个星号;
二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)
复制代码
function* read() {
console.log(1);
let a = yield '第一次';
console.log(a);
let b = yield '第二次'
console.log(b);
return b;
}
let it = read();
console.log(it.next('111')); // {value:'第一次',done:false}
console.log(it.next('222')); // {value:'第二次',done:false}
console.log(it.next('333')); // {value:'333',done:true}
复制代码
Generator 应用场景 主要还是要与promise 配合一起使用
Generator 和 co:
nodejs出现了 co 模块,它基于 ES6 的 generator 和 yield ,让我们能用同步的形式编写异步代码。
复制代码
co(function *() {
var data = yield $.get('/api/data');
console.log(data);
var user = yield $.get('/api/user');
console.log(user);
var products = yield $.get('/api/products');
console.log(products);
});
复制代码
以上的 Promise 和 generator 最初创造它的本意都不是为了解决异步流程控制。其中 Promise 是一种编程思想,用于“当xx数据准备完毕,
then执行xx动作”这样的场景,不只是异步,同步代码也可以用 Promise。而 generator 在 ES6 中是迭代器生成器,被 TJ 创造性的拿来做异步流程
控制了
复制代码
4.ES7 async / await 语法糖
async / await co + generator的语法糖
async / await 解决的问题有哪些?
1.回调地狱
2.并发执行异步,在同一时刻同步返回结果 Promise.all()
3.解决了返回值的问题
4.可以实现代码的try/catch;
复制代码
async function r(){
try{
let content1 = await read('./2.promise/100.txt','utf8');
let content2 = await read(content1,'utf8');
return content2;
}catch(e){ // 如果出错会catch
console.log('err',e)
}
}
// async函数返回的是promise,
r().then(function(res){
console.log(res);
},function(err){
console.log(err);
})
复制代码
Promise 扮演着一个承上启下的作用,非常关键,我们动手写一个自己的Promise!
---->(fulfilled)成功态 --->resolve();
|
new Promise ----(pending) 初始化为等待态
|
---->(rejected)失败态 --->reject();
复制代码
function Promise(executor) {
let _this = this;
//promise的 三种状态
_this.status = 'pending'; //等待态 初始值 实例化promise一开始时是等待状态 唯一性
_this.value = undefined; //成功态 表示成功时要传的值 初始默认值
_this.error = undefined; //失败态 表示失败时要传的值
_this.onFufilledCallbacks = []; // 存放then成功的回调函数
_this.onRejectedCallbacks = []; // 存放then失败的回调函数
//成功的方法
function resolve(value) {
if (_this.status === 'pending') {
_this.status = 'fulfilled'; //当调用 resolve()时改为成功态
_this.value = value;
//遍历 存放then成功的回调函数 并一次调用
_this.onFufilledCallbacks.forEach(function (itemFn) {
itemFn();
});
};
};
//失败的方法
function reject(error) {
if (_this.status === 'pending') {
_this.status = 'rejected'; //当调用 reject()时改为失败态
_this.error = error;
_this.onRejectedCallbacks.forEach(function (itemFn) {
itemFn();
});
}
};
//处理 实例化时 throw new err('错误') 需要走reject();!!!
try {
executor(resolve, reject); //executor执行器,包含两个参数,分别是resolve() 和reject(),new Promise这个executor就会立即执行
} catch (err) { //捕获时发生异常,就直接失败
reject(err)
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 有可能这里返回的x是别人的promise
// 尽可能允许其他乱写
if (promise2 === x) { //这里应该报一个类型错误,有问题
return reject(new Typeerr('循环引用了'))
}
// 看x是不是一个promise,promise应该是一个对象
let called; // 表示是否调用过成功或者失败
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
try { // {then:1}
let then = x.then;
if (typeof then === 'function') {
// 成功
then.call(x, function (y) {
if (called) return
called = true
// y可能还是一个promise,在去解析直到返回的是一个普通值
resolvePromise(promise2, y, resolve, reject)
}, function (err) { //失败
if (called) return
called = true
reject(err);
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true;
reject(e);
}
} else { // 说明是一个普通值1
resolve(x); // 表示成功了
}
}
// then方法注册 当resolve(成功)/reject(失败)的回调函数
Promise.prototype.then = function (onFufilled, onRejected) {
//成功和失败默认不传给一个函数
onFufilled = typeof onFufilled === 'function' ? onFufilled : function (value) {
return value;
}
onRejected = typeof onRejected === 'function' ? onRejected : function (err) {
throw err;
}
let _this = this;
let promise2; //返回的promise 处理 .then() 的链式调用 返回一个新的promise对象
if (_this.status === 'fulfilled') {
promise2 = new Promise(function (resolve, reject) {
// 当成功或者失败执行时有异常那么返回的promise应该处于失败状态
// x 是return的返回值 可能是一个promise 也可能是一个普通值
setTimeout(function () { //用setTimeout来模拟异步,真正的es6promise不是用setTimeout来实现的
try {
let x = onFufilled(_this.value)
// x可能是别人promise,写一个方法统一处理
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err);
}
});
});
};
if (_this.status === 'rejected') {
//当成功或者失败执行时有异常时 那么返回的 promise2应该处于失败状态
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onRejected(_this.error); // x 是return的返回值 可能是一个promise 也可能是一个普通值
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err);
}
});
});
};
//当调用 then 时 有可能没成功也没失败 还是pending 需要把回调先存起来
if (_this.status === 'pending') {
promise2 = new Promise(function (resolve, reject) {
_this.onFufilledCallbacks.push(function () {
setTimeout(function () {//用setTimeout来模拟异步,真正的es6promise不是用setTimeout来实现的
try {
let x = onFufilled(_this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
});
});
_this.onRejectedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onRejected(_this.error);
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
});
});
});
};
return promise2
}
复制代码
使用自己的封装的promise:
复制代码
let p = new Promise(function(resolve,reject) {
resolve('success');
});
p.then(function(data) {
console.log('成功:',data)
return 'zhanghw'
},function(err) {
console.log('失败:',err)
}).then(function(data) {
console.log(data)
},function(err) {
console.log(err)
})
复制代码
打印结果:
成功: success
zhanghw
复制代码