Promise
Promise
是一个对象,它代表了一个异步操作的最终完成或者失败。因为大多数人仅仅是使用已创建的 Promise 实例对象,所以本教程将首先说明怎样使用 Promise,再说明如何创建 Promise。
本质上 Promise 是一个函数返回的对象,我们可以在它上面绑定回调函数,这样我们就不需要在一开始把回调函数作为参数传入这个函数了。
一个 Promise
必然处于以下几种状态之一:
- 待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled): 意味着操作成功完成。
- 已拒绝(rejected): 意味着操作失败。
待定状态的 Promise 对象要么会通过一个值被兑现(fulfilled),要么会通过一个原因(错误)被拒绝(rejected)。当这些情况之一发生时,我们用 promise 的 then 方法排列起来的相关处理程序就会被调用。如果 promise 在一个相应的处理程序被绑定时就已经被兑现或被拒绝了,那么这个处理程序就会被调用,因此在完成异步操作和绑定处理方法之间不会存在竞争状态。
基本使用
// Promise
// 需要传递一个执行期函数 executor function
let Op = new Promise(()=>{
});
Promise 和里面的函数是同步执行
// Promise
// 需要传递一个执行期函数 executor function
let Op = new Promise(()=>{
// 里面的函数和 Promis 是同步执行
console.log('0')
});
console.log('1')
// 先打印 0
// 后打印 1
从上面的例子中我们就知道 Promise 和里面的函数是同步执行
异步操作
// Promise
// 需要传递一个执行期函数 executor function
// 可以传递参数 resolve, reject 分来用来触发成功和失败的回调
// 使用 setTimeout 模拟异步操作
let op = new Promise((resolve, reject)=>{
// 异步操作
setTimeout(()=>{
Math.random() * 100 > 60 ? resolve("ok"):reject('no')
},1000)
});
// 异步执行
op.then((val)=>{
console.log(val); // 输出ok
},(err)=>{
console.log(err); // 输出no
})
在上面的代码中我们使用 setTimeout
来模拟一个异步请求,在 Promise
中有两个参数 resolve
,reject
分别用来触发成功和失败的回调。
// Promise
// 需要传递一个执行期函数 executor function
// 可以传递参数 resolve, reject 分来用来触发成功和失败的回调
let op = new Promise((resolve, reject)=>{
console.log(0)
resolve(1);
});
// 异步执行
op.then((val)=>{
console.log(val); // 输出ok
},(err)=>{
console.log(err);// 输出on
})
console.log(2);
// 输出 0 2 1
是因为 .then
是异步执行,被当成微任务。在上面我们也讲了 resolve
,reject
分别用来触发成功和失败的回调函数。
// Promise
// 需要传递一个执行期函数 executor function
// 可以传递参数 resolve, reject 分来用来触发成功和失败的回调
setTimeout(()=>{
console.log('setTime');
},0)
let op = new Promise((resolve, reject)=>{
console.log(0)
resolve(1);
});
// 异步执行
//
op.then((val)=>{
console.log(val); // 输出ok
},(err)=>{
console.log(err);// 输出on
})
console.log(2);
// 输出 0 2 1 setTime
我们都知道在 JavaScript 的任务队列中分为宏任务和微任务,我们通过 setTimeout()
或者触发事件、时间处理函数、ajax的回调都是放在宏任务。宏任务会被优先放到任务队列(task queue)里面去 ,微任务会被后被放入任务对列之中,但是微任务有优先执行权,所以微任务会优先被执行。
Promise 的链式操作
// Promise
// 需要传递一个执行期函数 executor function
// 可以传递参数 resolve, reject 分来用来触发成功和失败的回调
// 使用 setTimeout 模拟异步操作
let op = new Promise((resolve, reject)=>{
// 异步操作
setTimeout(()=>{
Math.random() * 100 > 60 ? resolve("ok"):reject('no')
},1000)
});
// 异步执行
op.then((val)=>{
console.log(val); // 输出ok
},(err)=>{
console.log(err); // 输出no
}).then((val)=>{
console.log("ok then2"+val); // 输出ok
},(err)=>{
console.log("no then2"+err); // 输出no
})
// 不管前面是输出的 ok 还是 no 都会输出 ok then2 undefined
// 前面的代码不抛出错误, 下面的都会输出 ok then2 undefined
在上面的代码中,不管是成功还是失败,都会输出 ok then2 undefined
,除非前面的代码抛出错误
// Promise
// 需要传递一个执行期函数 executor function
// 可以传递参数 resolve, reject 分来用来触发成功和失败的回调
// 使用 setTimeout 模拟异步操作
let op = new Promise((resolve, reject)=>{
// 异步操作
setTimeout(()=>{
Math.random() * 100 > 60 ? resolve("ok"):reject('no')
},1000)
});
// 异步执行
op.then((val)=>{
console.log(val); // 输出ok
return 20
},(err)=>{
console.log(err); // 输出no
return 30
}).then((val)=>{
console.log("ok then2"+val); // 输出ok
},(err)=>{
console.log("no then2"+err); // 输出no
})
// 不管前面是输出的 ok 还是 no 都会输出 ok then2 20
// 前面的代码不抛出错误, 下面的都会输出 ok then2 20
// 如果抛出错误,这会输出 ok then2 30
上面的代码中加入了一个 return 20/30
会成为后面链式调用的参数,返回值会成为下一个 then
注册函数的执行参数。
如果返回的是一个 Promise
对象
// Promise
// 需要传递一个执行期函数 executor function
// 可以传递参数 resolve, reject 分来用来触发成功和失败的回调
// 使用 setTimeout 模拟异步操作
let op = new Promise((resolve, reject)=>{
// 异步操作
setTimeout(()=>{
Math.random() * 100 > 60 ? resolve("ok"):reject('no')
},1000)
});
// 异步执行
op.then((val)=>{
console.log(val); // 输出ok
return new Pormise((resolve,reject)=>{
resolve('newPromise, ok')/reject('newPromise, no')
})
},(err)=>{
console.log(err); // 输出no
return 30
}).then((val)=>{
console.log("ok then2"+val); // 输出ok
},(err)=>{
console.log("no then2"+err); // 输出no
})
// 不管前面是输出的 ok 还是 no 都会输出 ok then2 newPromise, ok
// 如果抛出错误,这会输出 ok then2 newPromise, no
下面的 .then
的执行结果会就根据第一个 .then
的结果去显示对应的内容,成功显示 ok then2 newPromise, ok
,失败显示 ok then2 newPromise, no
。
处理回调地狱
let fs = require('fs');
function readFile(path){
return new Promise((resolve, reject)=>{
fs.readFile(path,'utf-8',(err,data)=>{
if(data){
resolve(data);
}
})
})
}
readFile(path).then((data)=>{
return readFile(data);
}).then((data)=>{
return readFile(data);
}).then((data)=>{
conosle.log(data);
})