/**
- @description 模仿信號機制
- // function A
- const demoSignal = createSignal(1000); // currentTime: 1552641398505
- await doSomething();
- await doSomethingElse();
- demoSignal.fulfill('hahah'); // currentTime: 1552641413819
- // function B
- const res = await demoSignal; // currentTime: 1552641398505
- console.log(res, Date.now()); // print 'haha, 1552641413819'
- @param {Number} timeout 超時時間,超過時間自動resolve
- @return {Signal} 信號 extends Promise fulfill
*/
exports.createSignal = function(timeout = 4000) {
let resolver;
const signal = new Promise(resolve => {
resolver = resolve;
});
signal.fulfill = resolver;
if (timeout) {
setTimeout(() => signal.fulfill(), timeout);
}
return signal;
};
使用
1做祝、創(chuàng)建信號量promise ctx.dingqiMarketQiangGouSignal = createSignal();
2报腔、其他地方需要等待這個信號量 就 const qiangGouDelData = ctx.dingqiMarketQiangGouSignal ? await ctx.dingqiMarketQiangGouSignal : [];
3、最后原始信號量執(zhí)行resolve ctx.dingqiMarketQiangGouSignal.fulfill(fulfillArray); 原等待的地方 獲得數(shù)據(jù)
總結(jié):
promise 遇到await 的時候一定要等到 resolve 執(zhí)行之后才會執(zhí)行下面的邏輯剖淀;
例如
setTimeout( function () {
console.log( 'setTimeout' )
}, 0 );
let a;
const b = new Promise( function ( resolve ) {
console.log( 'promise1' )
a = resolve;
} ).then( function () {
console.log( 'promise2' )
} );
async function foo() {
console.log( '001' )
await b;
console.log( '002' )
}
foo();
a(); // 只有執(zhí)行了 這行 002 才能打印
console.log( 'script end' );
[Log] promise1
[Log] 001
[Log] script end
[Log] promise2
[Log] 002
< undefined
[Log] setTimeout
setTimeout( function () {
console.log( 'setTimeout' )
}, 0 );
new Promise( function ( resolve ) {
console.log( 'promise1' )
resolve();
} ).then( function () {
console.log( 'promise2' )
} )
console.log( 'script end' )
[Log] promise1
[Log] script end
[Log] promise2
< undefined 打印的原因?
[Log] setTimeout
Promise 使用纤房,Promise定義后會立馬執(zhí)行
async function foo() {
console.log( 'foo start' )
await bar()
console.log( 'foo end' )
}
async function bar() {
console.log( 'bar' )
}
console.log( 'script start' )
setTimeout( function () {
console.log( 'setTimeout' )
}, 0 )
foo();
const a = new Promise( function ( resolve ) {
console.log( 'promise1' )
return resolve('222');
} );
a.then( function (v) {
console.log( 'promise2'+v )
} );
console.log( 'script end' )
[Log] script start
[Log] foo start
[Log] bar
[Log] promise1
[Log] script end
[Log] promise2222
[Log] foo end
< undefined
[Log] setTimeout
解析:
對于await來說纵隔,分2個情況
不是promise對象
是promise對象
如果不是 promise , await會阻塞后面的代碼,先執(zhí)行async外面的同步代碼炮姨,同步代碼執(zhí)行完捌刮,再回到async內(nèi)部,把這個非promise的東西舒岸,作為 await表達式的結(jié)果绅作;
如果它等到的是一個 promise 對象,await 也會暫停async后面的代碼蛾派,先執(zhí)行async外面的同步代碼俄认,等著 Promise 對象 fulfilled,然后把 resolve 的參數(shù)作為 await 表達式的運算結(jié)果洪乍。
我們要先明確一些基本概念眯杏,在Js 中,有兩類任務隊列:宏任務隊列(macro tasks)和微任務隊列(micro tasks)壳澳。宏任務隊列可以有多個岂贩,微任務隊列只有一個。
宏任務:script(全局任務), setTimeout, setInterval, setImmediate, I/O, UI rendering.
微任務:process.nextTick, Promise, Object.observer, MutationObserver.
下面來根據(jù)線程說下具體的執(zhí)行順序巷波,我們都知道js是單線程的萎津,他在執(zhí)行的時候把各種任務放在隊列里卸伞,會依此執(zhí)行,但是根據(jù)以上的信息锉屈,我們先把任務流模擬下:
首先是2個函數(shù)聲明荤傲,雖然有async關(guān)鍵字,但不是調(diào)用我們就不看部念。然后首先是打印同步代碼 console.log('script start')弃酌;//這是全局script任務,作為宏任務1
然后將將setTimeout放入宏任務隊列儡炼,這里是宏任務2
調(diào)用foo妓湘,打印 同步代碼 console.log( foo start' );
接著 await async2(),我們來分析下它做了什么:
先得到await右側(cè)表達式的結(jié)果乌询。執(zhí)行async2()榜贴,打印同步代碼console.log('bar'), 參考上面的結(jié)論,這時候是進程被阻塞的妹田,被阻塞后唬党,要執(zhí)行async之外的代碼的,輸出promise1鬼佣,??這里只是把promise2推入微任務隊列驶拱,并沒有執(zhí)行。微任務會在當前宏任務的同步代碼執(zhí)行完畢晶衷,才會依次執(zhí)行蓝纲,然后foo end,最后return Promise.resolve(undefined);
-
對于 await Promise.resolve(undefined) 如何理解呢晌纫?
根據(jù) MDN 原話我們知道
如果一個 Promise 被傳遞給一個 await 操作符税迷,await 將等待 Promise 正常處理完成并返回其處理結(jié)果。
在我們這個例子中锹漱,就是Promise.resolve(undefined)正常處理完成箭养,并返回其處理結(jié)果。那么await bar()就算是執(zhí)行結(jié)束了哥牍。
目前這個promise的狀態(tài)是fulfilled毕泌,等其處理結(jié)果返回就可以執(zhí)行await下面的代碼了。
那何時能拿到處理結(jié)果呢嗅辣?
回憶平時我們用promise懈词,調(diào)用resolve后,何時能拿到處理結(jié)果辩诞?是不是需要在then的第一個參數(shù)里坎弯,才能拿到結(jié)果。(調(diào)用resolve時,會把then的參數(shù)推入微任務隊列抠忘,等主線程空閑時撩炊,再調(diào)用它)
所以這里的 await Promise.resolve() 就類似于:
把then的第一個回調(diào)參數(shù) (undefined) => {} 推入微任務隊列。
then執(zhí)行完崎脉,才是await far()執(zhí)行結(jié)束拧咳。
await far()執(zhí)行結(jié)束,才能繼續(xù)執(zhí)行后面的代碼.
此時當前宏任務1都執(zhí)行完了囚灼,要處理微任務隊列里的代碼骆膝。
微任務隊列,先進先出的原則灶体,
執(zhí)行微任務1阅签,打印promise2
執(zhí)行微任務2,沒什么內(nèi)容..
但是微任務2執(zhí)行后蝎抽,await far()語句結(jié)束政钟,后面的代碼不再被阻塞,所以打印
console.log( 'foo end' )
等到宏任務1執(zhí)行完樟结,還有它隊列里的為任務也執(zhí)行完畢养交,就開始宏任務2,打印結(jié)果:setTimeout
;(function (root) {
/**
- InterceptorManage, 代表一個攔截器瓢宦,用于發(fā)起jsonp前及響應之后的統(tǒng)一操作
- @constructor
- @param {array} handlers - 存放數(shù)組
/
class InterceptorManager {
constructor () {
this.handlers = []
}
/*- 添加promise(resolve, reject)對象至數(shù)組
- @param {function} fulfilled - promise resolve函數(shù)
- @param {function} rejected - promise rejected函數(shù)
- @return {number} id - 在handlers數(shù)組中的位置
/
use (fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
})
return this.handlers.length - 1
}
/* - 通過id刪除handlers中的項
- @param {number} id - use函數(shù)返回的所在handlers的下標
*/
eject (id) {
if (this.handlers[id]) {
this.handlers[id] = null
}
}
}
jsonp.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
}
/** 解析器parser
- parser.toJSON() 將對象轉(zhuǎn)換成字符串
- parser.toObject() 將鍵值對字符串轉(zhuǎn)換成數(shù)組
/
let parser = {
/*- 將對象轉(zhuǎn)換成特定字符串格式
- @param {object} obj - 目標對象
- @param {string} separator - 鍵值對之中的分隔符碎连,默認為'='
- @param {string} delimiter - 鍵值對連接的分隔符,默認為'&'
- @example
- // return a=1&b=2
- parser.toJSON({a:1, b: 2})
- // return a=1?b=2
- parser.toJSON({a:1, b: 2}, undefined, '?')
/
toJSON (obj = {}, separator = '=', delimiter = '&') {
let result = ''
Object.entries(obj).forEach(([key, value], index) => {
result += ((index === 0) ? '' : delimiter) + key + separator + value
})
return result
},
/* - 將鍵值對字符串轉(zhuǎn)換成對象
- @param {string} jsonstr - 要轉(zhuǎn)換的鍵值對字符串
- @param {*} separator - 鍵值對之中的分隔符驮履,默認為'='
- @param {*} delimiter - 鍵值對連接的分隔符鱼辙,默認為'&'
- @example
- // return {a: '1', b: '2'}
- parser.toObject('a=1&b=2')
- // return {a: '1', b: '2'}
- parser.toObject('a:1?b:2', ':', '?')
*/
toObject (jsonstr, separator = '=', delimiter = '&') {
if (typeof jsonstr !== 'string') {
throw new Error('first parameter must be a string')
}
return jsonstr.split(delimiter).reduce((result, item) => {
result[item.split(separator)[0]] = item.split(separator)[1]
return result
}, {})
}
}
/** 定義空函數(shù) */
let noop = () => {}
/**
- promise 實現(xiàn)的jsonp函數(shù)
- @param {string} url - jsonp 請求地址
- @param {object} options - jsonp 請求參數(shù)對象,其中屬性jsonpCallback 為函數(shù)名
/
function jsonp (url, options = {jsonpCallback: 'callback'}) {
// 用法 Promise.resolve https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
let promise = Promise.resolve(options)
let dispatchRequest = function () {
/* 返回promise疲吸, 內(nèi)部為傳統(tǒng)jsonp的用法及代碼 */
return new Promise((resolve, reject) => {
let s = document.createElement('script')
let index = url.indexOf('?')
if (index > -1) {
Object.assign(options, parser.toObject(url.substring(index + 1)))
url = url.substring(0, index)
}
let cName = 'jsonp' + Date.now()
options[options['jsonpCallback']] = cName
delete options['jsonpCallback']
window[cName] = function (res) {
resolve(res)
}
s.src = url + '?' + parser.toJSON(options)
document.body.appendChild(s)
s.remove()
s.onerror = function (err) {
rejected(err)
}
})
}
let chain = [dispatchRequest, noop]
// request dispatch前置處理
jsonp.interceptors.request.handlers.forEach((interceptor) => {
chain.unshift(interceptor.fulfilled, interceptor.rejected)
})
// response dispatch數(shù)據(jù)之后處理
jsonp.interceptors.response.handlers.forEach((interceptor) => {
chain.push(interceptor.fulfilled, interceptor.rejected)
})
while (chain.length) {
// 循環(huán)處理 里面的所有邏輯
promise = promise.then(chain.shift(), chain.shift())
}
return promise
// 第一種調(diào)用
// jsonp('https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su', {
// wd: 'github',
// jsonpCallback: 'cb'
// }).then((res) => {
// console.log(res)
// })
// 第二種調(diào)用
//Add a request interceptor
// jsonp.interceptors.request.use((config) => {
// // Do something before request is send
// return config
// }, (error) => {
// // Do something with request error
// return Promise.reject(error)
// })
// //Add a response interceptor
// jsonp.interceptors.response.use((response) => {
// // Do something with response data
// return response
// }, (error) => {
// // Do something with response error
// return Promise.reject(error)
// })
// 示例
// var thenable = { then: function(resolve, reject) {
// reject('444');
// // throw new TypeError("Throwing");
// // resolve("Resolving");
// }};
// var p2 = Promise.resolve(thenable);
// p2.then(function(v) {
// // 不會被調(diào)用
// }, function(e) {
// console.log(e); // TypeError: Throwing
// });
}
/** 模塊化封裝 */
if (typeof define !== 'undefined' && define.amd) {
define([], function () {
return jsonp
})
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = jsonp
} else {
root.jsonp = jsonp
}
})(this)